题目描述

✅ 面试题 05.03. 翻转数位

image-20260929011956132

题意分析

可以把一个 32 位整数中的一个 0 翻成 1,求能得到的最长连续 1 的长度。处理的是固定的 32 位表示,包括高位零和负数的补码位,答案不会超过 32。

一段连续位能通过这次操作变成全 1,当且仅当其中至多有一个 0。因此问题转化为:在这 32 位中,寻找最多包含一个零的最长连续区间。只有一个零就翻转它;没有零的区间本来就全为一,全 1 输入的答案就是 32。

不能在最高有效位处停止,因为其上方的零也可能用来延长连续段。固定扫描还自然覆盖 num=0,此时能得到的最长长度为 1。

解法:固定宽度滑动窗口

核心思路

[!blue]

从低位向高位扫描,用 [j,i] 作为窗口,cnt 表示其中零的个数。连续位反向读取仍然连续,因此这个方向不会改变要求的最大长度。

((num >> i) & 1) 取得原数第 i 位,再与 1 异或,就得到“这一位是否为零”的计数贡献。加入当前位后,如果 cnt>1,就不断移出左端位,并扣掉它对零数的贡献,直到窗口重新合法。

更新答案时,j 是以当前 i 结尾的最靠左合法起点。收缩只在窗口含有两个零时发生,并在刚刚移出更早的零后停止;更靠左的起点仍会包含这两个零。以前已经排除的起点也不会因右侧再加入字符而重新合法,所以左端无需回退。

因而 i-j+1 是以当前位结尾的最长可行长度。每个连续区间都有一个右端点,枚举所有 i 并取这些长度的最大值,就不会漏掉最优区间。两个指针只向前移动,窗口计数始终与当前区间一致。

解题步骤

  • 初始化左端点 j = 0、窗口零数 cnt = 0、答案 answer = 0。
  • 用 i = 0..31 从低位到高位扫描;连续区间反向读取仍然连续,不影响长度。
  • 取出第 i 位,若为 0 就让 cnt 加一。
  • 当 cnt > 1 时移动 j,移出的位若为 0,同时把 cnt 减一。
  • 此时窗口合法,用其长度更新答案。

对负数,算术右移虽然会补符号位,但再与 1 相与,只取出原来的指定位置。只扫描 0..31,不会把更宽机器字上的符号扩展算进答案。

代码实现

// 窗口中最多允许一个 0;这个 0 就是要翻转的位。
class Solution {
    public int reverseBits(int num) {
        int answer = 0;
        int cnt = 0;

        for (int i = 0, j = 0; i < 32; ++i) {
            cnt += num >> i & 1 ^ 1;

            while (cnt > 1) {
                cnt -= num >> j & 1 ^ 1;
                ++j;
            }

            answer = Math.max(answer, i - j + 1);
        }

        return answer;
    }
}
// 窗口中最多允许一个 0;这个 0 就是要翻转的位。
func reverseBits(num int) (answer int) {
    var cnt, j int
    for i := 0; i < 32; i++ {
        cnt += num>>i&1 ^ 1
        for cnt > 1 {
            cnt -= num>>j&1 ^ 1
            j++
        }
        answer = max(answer, i-j+1)
    }
    return
}

复杂度分析

  • 时间复杂度:对 32 位整数固定扫描 32 次,严格来说是 $O(1)$;推广到长度为 b 的位串是 $O(b)$,左右指针都至多移动 b 次。
  • 空间复杂度:$O(1)$,只维护两个指针、窗口零数和答案。

关键点总结

[!green]

  • 一次翻转预算转化为窗口内至多一个零。
  • 收缩到刚好合法,才能保留当前右端点下的最长区间。
  • 固定扫描 32 位,统一处理高位零、负数与全一情况。

易错点总结

[!yellow]

  • 只扫描到最高有效位,会漏掉可翻转的高位零。
  • 收缩后要同步更新 cnt,不能只移动左指针。
  • 位运算表达式的含义是 ((num >> i) & 1) ^ 1,先提取单个位,再把它转换为零的数量贡献。

相似题目

题目 难度 关联与区别
1004. 最大连续1的个数 III 中等 同样寻找至多包含限定数量零的最长窗口,本题只扫描整数低 32 位且最多翻一个零。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/46926442
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!