LeetCode 397:整数替换(贪心问题) —— 题解

👋 欢迎阅读

🎯 欢迎来到「整数替换」题解之旅! 本文将带你从"通过加减和除法将整数变为1"这一数字变换问题出发,深入理解贪心算法 + 位运算 的巧妙运用,并掌握如何通过分类讨论奇偶性来高效求出最小操作次数。

在开始之前,建议你先:

  • 了解题目背景 :这是 LeetCode 397 题,给定正整数 nn,若为偶数则除以 2,若为奇数则可选择加 1 或减 1,求将 nn 变为 1 所需的最小操作次数 。本质上,这是一个最短路 问题,但由于数据范围可达 231−1231−1,我们需要用贪心策略而非 BFS,核心在于对奇数的加/减 1 做出最优选择------当 n≡3(mod4)n≡3(mod4) 且 n>3n>3 时,加 1 更优(因为能更快地产生连续除法);否则减 1 更优。

  • 明确学习目标 :掌握贪心决策规则------偶数直接除以 2;奇数时,判断 n%4n%4 的值:若为 3 且 n>3n>3,选择加 1(n+1n+1 会变成 4 的倍数,连续除以 2 两次),否则选择减 1(使数变小且不影响后续的整除效率)。同时理解为什么 n=3n=3 是特例(3→2→13→2→1 需 2 步,优于 3→4→2→13→4→2→1 的 3 步)。

  • 准备好环境 :建议在本地 IDE 或 LeetCode 在线编辑器中打开代码,边看边运行,亲手验证示例(如 n = 7 输出 4n = 8 输出 3)。

本文将从问题转化、贪心决策规则推导(模 4 分析)、位运算优化 到代码实现,层层递进。即使你对贪心算法还不熟悉,我们也会从"怎样让数字尽快变成 1"这一直觉出发,让你轻松抓住核心思想 ------偶数直接除以 2 是最快的;奇数时,选择加 1 或减 1 要看哪种方式能制造出更多连续的除以 2 机会。现在,让我们一起通过最少的变换,把这个整数"归约"到 1 吧! 🔢🔄


一、题目

397. 整数替换 - 力扣(LeetCode)

二、做题思路

1. 问题分析(前置分析)

给定正整数 n,允许两种操作:

  • n 为偶数,只能 除以 2

  • n 为奇数,可以选择 加 1减 1

    目标是用 最少的操作次数n 变为 1

    核心贪心思想 :偶数时直接除以 2 是最优的(唯一选择)。奇数时 ,加 1 或减 1 都会变成偶数,随后可除以 2。为了尽快缩小数字,应优先选择能让后续连续除以 2 更多次的操作 ,即尽量使结果成为 4 的倍数 (因为 4 的倍数可以连续除以 2 两次)。因此根据 n % 4 的值决定加 1 还是减 1。


2. 贪心策略(核心决策规则)

  • 初始化操作次数 ret = 0

  • 循环直到 n == 1

    • n 为偶数:直接除以 2ret++

    • n 为奇数:

      • 特殊情况 :若 n == 3,则 减 1 再除以 2 (即 3 -> 2 -> 1)只需 2 步,比加 1 的 3 步更优,单独处理 ret += 2, n = 1

      • n % 4 == 1 :则 减 1 后得到 4 的倍数,可连续除以 2 两次,执行 n = (n - 1) / 2ret += 2

      • n % 4 == 3 :则 加 1 后得到 4 的倍数,执行 n = n / 2 + 1(等价于 (n+1)/2),ret += 2

  • 返回 ret


3. 正确性说明(简单版本)

偶数时除以 2 是唯一操作,必然最优。对于奇数,减 1 或加 1 都会产生偶数,随后可以除以 2。为了最小化总步数,应选择那个能产生更多连续除以 2 机会的操作

  • n % 4 == 1 时(如 5, 9),减 1 后变为 4 的倍数,能连续除以 2 两次;加 1 后变为 n+1,只多一次除以 2,显然减 1 更优。

  • n % 4 == 3 时(如 7, 11),加 1 后变为 4 的倍数,能连续除以 2 两次;减 1 后变为 n-1,只多一次除以 2,加 1 更优。

  • n == 3 是特例,因为加 1 得到 4 虽能除以 2 两次,但总共需 3 步;减 1 得到 2 再除以 2 只需 2 步,因此单独处理。

    这种 "尽可能产生 4 的倍数" 的贪心策略,保证了每次操作都选择当前局部最优,最终得到全局最优解。


4. 实现细节(边界防护)

  • 使用 long long 类型存储 n,防止 n+1 溢出(但题目 n ≤ 2^31-1n+1 可能溢出,需注意,但代码中直接对奇数操作,n/2+1 等不会溢出,不过建议使用 long long 更安全)。

  • 循环条件为 n > 1,逐步减少直到 1。

  • 偶数时直接除以 2。

  • 奇数时先判断 n == 3,再根据 n % 4 决定操作,每次操作计为 2 步(因为加/减 1 和随后的除以 2 共两次操作)。

  • 最终返回 ret


5. 返回值(目标映射)

返回 ret,即 n 变为 1 所需的最少操作次数

四、代码

cpp 复制代码
class Solution 
{
public:
    int integerReplacement(int n) 
    {
        // 贪心策略:对于偶数,直接除以2(最优);对于奇数,根据 n % 4 的值决定加1还是减1,
        // 因为加1或减1都会产生偶数,随后可以进行除以2操作,而不同的选择会影响后续的步数。
        // 数学结论:
        //   - 当 n % 4 == 1 时,减1后除以2比加1后除以2更优(因为减1后变成4的倍数,能连续除以2多次);
        //   - 当 n % 4 == 3 时,加1后除以2更优(但 n == 3 是个例外,此时减1再除以2只需2步,而加1需要3步)。
        // 因此贪心处理:优先让 n 变为能被4整除的数(或接近4的倍数),以加速除以2的过程。
    int ret = 0;   // 操作次数

    while (n > 1) 
    {
        if (n % 2 == 0) 
        {
            // 偶数:直接除以2,操作一次
            n = n / 2;
            ret++;
        } 
        else   // n 为奇数
        {
            // 特殊情况:n == 3 时,先减1再除以2(即 3 -> 2 -> 1)只需2步,
            // 而加1再除以2(3 -> 4 -> 2 -> 1)需要3步,所以单独处理。
            if (n == 3) 
            {
                ret += 2;   // 两步操作:n-1 然后除以2,直接变为1
                n = 1;
            } 
            // 如果 n % 4 == 1,则 n-1 后能被4整除,可以连续除以2两次,减少总步数
            else if (n % 4 == 1) 
            {
                // 执行两次操作:先减1,再除以2(相当于 (n-1)/2,但为了统计步数,直接算两次)
                n = (n - 1) / 2;
                ret += 2;
            } 
            // 如果 n % 4 == 3,则 n+1 后能被4整除,同样能连续除以2两次,
            // 但操作顺序是先加1再除以2,等价于 n/2 + 1(整数除法向下取整,但这里 n 为奇数,n/2 为整数部分)
            // 注意:加1再除以2 = (n+1)/2,因为 n 是奇数,(n+1)/2 正好等于 n/2 + 1(整数除法)。
            else if (n % 4 == 3) 
            {
                n = n / 2 + 1;   // 等价于 (n+1)/2
                ret += 2;
            }
        }
    }

    // 返回最少操作次数
    return ret;
}
};

五、流程图

六、正确性说明(详细版)

步骤 1:符号与问题建模
复制代码
+----------------------------------------------------+
|  输入:正整数 n                                     |
|  操作:偶数 -> n/2;奇数 -> n+1 或 n-1             |
|  目标:使 n 变为 1 的最少操作次数                   |
+----------------------------------------------------+
                    |
                    v
+----------------------------------------------------+
|  贪心策略:                                         |
|  ① 若 n 为偶数,直接除以2(**唯一可行操作**)。    |
|  ② 若 n 为奇数:                                    |
|     - 若 n == 3,则 n-1 后除以2 只需两步,         |
|       而 n+1 需要三步,因此选择 n-1。               |
|     - 若 n % 4 == 1,则 n-1 后得到 4 的倍数,       |
|       能连续除以2两次,优于 n+1(只除一次),       |
|       因此选择 n-1。                                |
|     - 若 n % 4 == 3,则 n+1 后得到 4 的倍数,       |
|       能连续除以2两次,优于 n-1,因此选择 n+1。     |
|  贪心实质:**尽可能让数变成4的倍数**,以连续进行   |
|  两次除以2操作,从而快速缩小数值。                   |
+----------------------------------------------------+
  • 偶数:只能除以2,无选择余地,贪心直接执行。

  • 奇数 :需要决策加1还是减1。选择能使下一步得到的数更小或含有更多因子2的方向,因为除以2是唯一能快速减小的操作。


步骤 2:关键性质 ------ 贪心选择的正确性(分类讨论与反证法)
复制代码
+----------------------------------------------------+
|  性质 1(偶数直接除2):对于偶数 n,任何最优解    |
|  的第一步都必须是 n/2。                             |
|  反证:若先加1或减1得到奇数,再操作,至少需要     |
|  两次才能回到偶数,而直接除以2只需一次,且数值更小,|
|  因此直接除以2必然最优。                           |
+----------------------------------------------------+
                     |
                     v
+----------------------------------------------------+
|  性质 2(n%4==1 时减1更优):若 n ≡ 1 (mod 4),   |
|  则 n-1 是 4 的倍数,可连续除以2两次;             |
|  而 n+1 只被2整除一次后变成奇数。因此减1比加1     |
|  能多进行一次除以2,总步数更少。                   |
|  反证:若选择加1,则得到 (n+1)/2,它比 (n-1)/2    |
|  大,且少了一次连续的除以2,后续不可能补偿。       |
+----------------------------------------------------+
                     |
                     v
+----------------------------------------------------+
|  性质 3(n%4==3 时加1更优,但 n=3 例外):         |
|  若 n ≡ 3 (mod 4) 且 n > 3,则 n+1 是 4 的倍数,  |
|  能连续除以2两次,而 n-1 只除一次就变奇数。        |
|  因此加1更优。                                     |
|  特殊 n=3:减1后直接到2,再除2到1,共2步;        |
|  而加1后3->4->2->1,共3步,故减1更优。             |
|  反证:对 n>3 若选择减1,则得到 (n-1)/2,它        |
|  是奇数,后续还要再处理,而加1得到 (n+1)/2 是偶数,|
|  可继续除2,显然加1更优。                          |
+----------------------------------------------------+

详细论证

  • 性质1 :偶数 n 只能进行除以2操作,其他操作(如先减1)会变成奇数,需要更多步骤才能回到偶数,且数值不会更小。因此直接除以2是唯一且最优的选择

  • 性质2 :设 n = 4k + 1。若选择 n-1 = 4k,则一次操作后得到 4k,再除以2得 2k,再除以2得 k(若 k>1 可继续)。而选择 n+1 = 4k+2,一次操作后得到奇数 2k+1,只能再执行一次加/减操作才能继续除以2。因此减1至少节省一步操作,所以减1更优。

  • 性质3 :设 n = 4k + 3k ≥ 1)。选择 n+1 = 4k+4 = 4(k+1),可连续除2两次;选择 n-1 = 4k+2 = 2(2k+1),除一次后得奇数 2k+1,无法立即再除2。故加1更优。但 n=3k=0)时,减1得 2,再除2得 1,只需2步;加1得 4,再除2得 2,再除2得 1,需3步,因此 n=3 必须单独处理,选择减1


步骤 3:归纳证明 ------ 贪心逐步构造全局最优
复制代码
+----------------------------------------------------+
|  初始:当前值 n,步数 ret=0。                      |
|  归纳假设:每次贪心操作后,剩余问题与原问题同构,  |
|  且贪心选择使得当前值在所有最优解中**最小**        |
|  (或为最优方向)。                                 |
+----------------------------------------------------+
                     |
                     v
+----------------------------------------------------+
|  每次循环:                                         |
|  - 若 n 为偶数,由性质1,**必须除以2**。           |
|  - 若 n 为奇数且 n>3:                              |
|    * 若 n%4==1,由性质2,**必须减1**(然后除以2)。|
|    * 若 n%4==3,由性质3,**必须加1**(然后除以2)。|
|  - 若 n==3,**必须减1**(直接到2,再除2)。       |
|  每次操作后 n 严格减小,归纳假设保持。              |
+----------------------------------------------------+
                     |
                     v
+----------------------------------------------------+
|  当 n == 1 时,ret 即为最小操作次数。               |
|  因为每一步的选择都是强制或最优的,不存在其他路径  |
|  能比贪心更少步数。                                 |
+----------------------------------------------------+

详细论证

  • 归纳基础 :当 n=1 时,无需操作,ret=0,显然最优。

  • 归纳步骤 :假设当前 n > 1,且之前所有贪心选择都使得 n 以最快速度减小。考虑所有可能的操作:

    • n 为偶数,唯一可执行的操作是除以2,因此这一步是强制性的,贪心必然最优。

    • n 为奇数且 n>3

      • n%4==1,由性质2,减1后能连续除2两次,而加1只能除一次,因此任何最优解的第一步都必须减1(否则需要额外步骤补偿),贪心选择正确。

      • n%4==3,由性质3,加1后能连续除2两次,减1则不行,因此任何最优解的第一步都必须加1,贪心选择正确。

    • n==3,分别计算两种路径:3->2->1(2步) vs 3->4->2->1(3步),减1明显更优,且是唯一的更优选择。

  • 每次操作后,n 的值都变为一个更小的正整数,且贪心选择不会排除任何潜在的最优解(因为我们已经证明了选择的必要性)。因此归纳成立 ,最终得到的 ret 即为全局最小操作次数。

🎯 闭幕

🎉 恭喜你完成了「整数替换」问题的学习!

为了巩固知识并进一步拓展,建议你:

🚀 动手实践

在 LeetCode 上提交代码,尝试不同的测试用例。

💡 深入思考

  • 本题采用 贪心 + 位运算 策略:偶数直接除以 2;奇数时,若 n % 4 == 1 则减 1,若 n % 4 == 3 则加 1(但 n == 3 例外)。为什么优先让奇数变为 4 的倍数能减少总操作次数? 你能从"连续除以 2"的角度解释吗?

  • 代码中 n == 3 时直接返回 ret += 2,即走 3 -> 2 -> 1 只需 2 步,而按 n%4==3 加 1 则需 3 步。如果不单独处理 3,贪心是否失效? 请用例子验证。

  • 本题也可以用 BFS记忆化 DFS 求解,但复杂度较高。贪心为什么能保证最优解? 你能找到反例证明贪心一定正确吗?

  • 如果 n 是偶数,直接除以 2 显然是最优的。但如果在除以 2 之前先加 1 或减 1,有没有可能更快到达 1? 为什么不可能?

📚 延伸挑战

  • 尝试用 动态规划BFS 实现本题,对比贪心的简洁性,在 n <= 1000 的小范围验证贪心结果是否正确。

如果你觉得本文对你有所帮助,欢迎:

👍 点赞 / 收藏

👤 关注作者,获取更多题解

💬 留言交流你的疑问或优化思路

祝你在 算法之路 上越走越稳,早日攻克每一道难题!下次见 🚀✨

相关推荐
奈斯先生Vector1 小时前
2026 开发者效能革命:基于创源AIGC 与开源生态的工程化实践、安全沙箱与代码智能体协同
人工智能·算法·架构·prompt·aigc
别动我齐刘海1 小时前
“三层同步审计”判定掉帧缺失
c语言·c++·人工智能·深度学习·学习·机器学习·机器人
Sagittarius_A*1 小时前
哈希与认证基础(一):哈希函数的安全目标:原像、第二原像与碰撞
算法·安全·信息安全·密码学·哈希算法
bbq粉刷匠2 小时前
ArrayList 整体认识
java·jvm·数据结构
坚持编程的菜鸟2 小时前
模拟实现strncat
c语言·算法·模拟实现strncat
wabs6662 小时前
关于哈希表【力扣1.两数之和的思考】
数据结构·算法·leetcode·散列表·图论
大辉狼_音频架构2 小时前
第 02 篇:时域的语言 —— 卷积、LTI 系统与冲激响应
算法·音视频开发
Hhy_11072 小时前
【从零开始学数据结构 ⑥】:二叉树之堆——打破规则的优先队列
c语言·开发语言·数据结构·学习·visual studio
蒸蒸yyyyzwd3 小时前
cpp 选手 转 ai 后端学习笔记
c++·transformer·ai编程