397. 整数替换

397. 整数替换

给定一个正整数 n ,你可以做如下操作:

    如果 n 是偶数,则用 n / 2替换 n 。
    如果 n 是奇数,则可以用 n + 1或n - 1替换 n 。

n 变为 1 所需的最小替换次数是多少?

示例 1:

输入:n = 8
输出:3
解释:8 -> 4 -> 2 -> 1

示例 2:

输入:n = 7
输出:4
解释:7 -> 8 -> 4 -> 2 -> 1
或 7 -> 6 -> 3 -> 2 -> 1

示例 3:

输入:n = 4
输出:2

提示:

    1 <= n <= 2^31 - 1

思路一:直接根据题意走,integerReplacement(n)表示将n化为1需要的最少步骤

        当n为奇数,则

             integerReplacement(n) = min(integerReplacement(n + 1), integerReplacement(n - 1)) + 1

        当n为偶数,则

             integerReplacement(n) = integerReplacement(n / 2) + 1

溢出处理:本题的n上限为2^31 - 1,因此n+1很有可能溢出

        A.最直接的处理方式是另写一个函数 int DFS(long long n)来替代 integerReplacement(int n)。

        B.观察本题,当n为奇数时,n-1或者n+1,n-1与n+1都是偶数,因此下一步一定是/2,所以当n为奇数时,存在两种路线

                1) integerReplacement(n / 2) + 2

                2) integerReplacement(n / 2 + 1) + 2,这里是为了防止溢出,因此先除后加

class Solution {
public:

    int integerReplacement(int n) {
        if(n == 1) return 0;
        if(n & 1){
            return min(integerReplacement((n >> 1) + 1), integerReplacement(n >> 1)) + 2;
        }else{
            return integerReplacement(n >> 1) + 1;
        }
    }
};

思路二:对于上述的算法,还是会发生重复计算的问题,因此可以修改为记忆化DFS。

class Solution {
public:

    unordered_map<int, int> hash_map;

    int integerReplacement(int n) {
        if(n == 1) return 0;
        if(hash_map.find(n) != hash_map.end()) return hash_map[n];
        if(n & 1){
            return hash_map[n] = min(integerReplacement((n >> 1) + 1), integerReplacement(n >> 1)) + 2;
        }else{
            return hash_map[n] = integerReplacement(n >> 1) + 1;
        }
    }
};

[注]:复杂度分析,这里简单提一下,由于每一次DFS都有右移运算,因此可以简单推出时间复杂度是O(lgn)。

思路三:使用数学的方法分析

    当 n 为偶数时,我们只有一种选择,就是使用一次操作将 n 变为 n / 2​ ;

    当 n 为奇数时,n 除以 4 的余数要么为 1,要么为 3 (为2就与n是奇数矛盾了)

        A.如果余数为 1,我们可以断定,应该将 n 变成 \frac{(n - 1)}{2}​。如果我们必须将 n 变成 \frac{n + 1}{2} 才能得到最优解,而 \frac{n + 1}{2}​​ 是奇数,那么:

[注]:若n%4==1,则我们可以将n表示为n=4*p+1,此时我们计算可知

\frac{n+1}{2}=\frac{4*p+1+1}{2}=2*p+1

因此说​​\frac{n + 1}{2}是奇数。由于是奇数,因此我们可以选择的下一步为-1或者+1

            如果下一步进行 -1 再除以 2 (需要两个步骤),则得到

\frac{\frac{n+1}{2}-1}{2}=\frac{n-1}{4}

那么从 ​(n - 1) / 2 可以除以 2 (只需要一个步骤) 得到同样的结果;

            如果下一步进行 +1 再除以 2 (需要两个步骤),则得到

\frac{\frac{n+1}{2}+1}{2}=\frac{n+3}{4}

那么从 (n - 1) / 2 可以除以 2 再 +1 (需要两个步骤) 得到同样的结果。

        因此当n%4==1时,将 n 变成 (n−1) / 2 再往后走所需要的步骤​数量总是不会多于将n变成(n + 1) / 2往后走需要的步骤数量​。

        B.如果余数为 3,我们可以断定,应该将 n 变成 (n + 1) / 2。如果我们必须将 n 变成(n−1) / 2才能得到最优解,而 (n−1) / 2​ 是奇数,那么:

            如果下一步进行 -1 再除以 2,得到 (n−3) / 4,那么从 (n+1) / 2 可以除以 2 再 -1 得到同样的结果。

            如果下一步进行 +1 再除以 2,得到 (n+1) / 4,那么从 (n+1) / 2​ 可以除以 2 得到同样的结果。

        因此当n%4==3时,将 n 变成 (n+1) / 2​ 再往后走所需要的步骤​数量总是不会多于将n变成(n−1) / 2往后走需要的步骤数量​。

        然而需要特别注意的是,如果当前是3,那么(n-1) / 2已经可以得到1,此时不应遵循上述规则。

class Solution {
public:

    int integerReplacement(int n) {
        int steps_num = 0;
        while(n != 1){
            if(n & 1){
                if(n % 4 == 1){
                    n >>= 1;
                }else{
                    if(n == 3){
                        n >>= 1;
                    }else{
                        n = (n >> 1) + 1;
                    }
                }
                steps_num += 2;
            }else{
                n = n / 2;
                steps_num += 1;
            }
        }
        return steps_num;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值