1009.十进制整数的反码

目标

每个非负整数 N 都有其二进制表示。例如, 5 可以被表示为二进制 "101",11 可以用二进制 "1011" 表示,依此类推。注意,除 N = 0 外,任何二进制表示中都不含前导零。

二进制的反码表示是将每个 1 改为 0 且每个 0 变为 1。例如,二进制数 "101" 的二进制反码为 "010"。

给你一个十进制数 N,请你返回其二进制表示的反码所对应的十进制整数。

示例 1:

输入:5
输出:2
解释:5 的二进制表示为 "101",其二进制反码为 "010",也就是十进制中的 2 。

示例 2:

输入:7
输出:0
解释:7 的二进制表示为 "111",其二进制反码为 "000",也就是十进制中的 0 。

示例 3:

输入:10
输出:5
解释:10 的二进制表示为 "1010",其二进制反码为 "0101",也就是十进制中的 5 。

说明:

  • 0 <= N < 10^9

思路

已知非负整数 N,求其二进制表示(不含前导零)取反后所表示的十进制数字。

如果直接 ~N 会对前导零取反,只需得到有效位的长度,然后与对应长度的二进制连续 1 异或即可。

代码


/**
 * @date 2026-03-11 8:47
 */
public class BitwiseComplement1009 {

    public int bitwiseComplement(int n) {
        if (n == 0) {
            return 1;
        }
        int l = 32 - Integer.numberOfLeadingZeros(n);
        return ((1 << l) - 1) ^ n;
    }

}

性能

1888.使二进制字符串字符交替的最少反转次数

目标

给你一个二进制字符串 s 。你可以按任意顺序执行以下两种操作任意次:

  • 类型 1 :删除 字符串 s 的第一个字符并将它 添加 到字符串结尾。
  • 类型 2 :选择 字符串 s 中任意一个字符并将该字符 反转 ,也就是如果值为 '0' ,则反转得到 '1' ,反之亦然。

请你返回使 s 变成 交替 字符串的前提下, 类型 2 的 最少 操作次数 。

我们称一个字符串是 交替 的,需要满足任意相邻字符都不同。

  • 比方说,字符串 "010" 和 "1010" 都是交替的,但是字符串 "0100" 不是。

示例 1:

输入:s = "111000"
输出:2
解释:执行第一种操作两次,得到 s = "100011" 。
然后对第三个和第六个字符执行第二种操作,得到 s = "101010" 。

示例 2:

输入:s = "010"
输出:0
解释:字符串已经是交替的。

示例 3:

输入:s = "1110"
输出:1
解释:对第二个字符执行第二种操作,得到 s = "1010" 。

说明:

  • 1 <= s.length <= 10^5
  • s[i] 要么是 '0' ,要么是 '1' 。

思路

有一个二进制字符串 s,每次操作:1.可以将首字母移动到末尾;2.或者将任意字符反转,求将 s 变为交替字符串所需的最小反转次数,即最少的操作 2 次数。

由于不考虑首尾移动的次数,可以将首尾相接,考虑字符串 s + s。交替字符串就两种,可以使用长度为 s.length 的滑动窗口计算反转的最小次数。

可以只考虑交替字符串 010101...,假设所需的反转次数为 k,那么 10101010... 所需的反转次数为 n - k,参考 1758.生成交替二进制字符串的最少操作数

010101... 这种交替字符串可以用下标的奇偶性来表示。移出窗口时,如果下标的奇偶性与元素值的奇偶性不同,说明差异个数减一。移入窗口同理,操作次数加一。

代码


/**
 * @date 2026-03-09 11:42
 */
public class MinFlips1888 {

    public int minFlips(String s) {
        int n = s.length();
        char[] chars = s.toCharArray();
        int ops = 0;
        for (int i = 0; i < n; ++i) {
            ops += (chars[i] ^ i) & 1;
        }
        int res = Math.min(ops, n - ops);
        if ((n & 1) == 0) {
            // 如果长度为偶数,操作1不会改变下标的奇偶性,比如 i,i + n 的奇偶性相同,后面循环中 ops 并不会发生变化
            return res;
        }
        for (int i = 0; i < n; ++i) {
            ops -= (chars[i] ^ i) & 1;
            ops += (chars[i] ^ (n + i)) & 1;
            res = Math.min(res, Math.min(ops, n - ops));
        }
        return res;
    }

}

性能

1680.连接连续二进制数字

目标

给你一个整数 n ,请你将 1 到 n 的二进制表示连接起来,并返回连接结果对应的 十进制 数字对 10^9 + 7 取余的结果。

示例 1:

输入:n = 1
输出:1
解释:二进制的 "1" 对应着十进制的 1 。

示例 2:

输入:n = 3
输出:27
解释:二进制下,1,2 和 3 分别对应 "1" ,"10" 和 "11" 。
将它们依次连接,我们得到 "11011" ,对应着十进制的 27 。

示例 3:

输入:n = 12
输出:505379714
解释:连接结果为 "1101110010111011110001001101010111100" 。
对应的十进制数字为 118505380540 。
对 10^9 + 7 取余后,结果为 505379714 。

说明:

  • 1 <= n <= 10^5

思路

拼接 1 ~ n 的二进制表示,将结果对应的十进制数对 10^9 + 7 取模。

遍历 1 ~ n 模拟拼接过程,将之前拼接的数字左移当前数字的 bitLength 并对 mod 取模。拼接 a b c 可以视为 (a * 2^bitLength(b) + b) * 2^bitLength(c) + c,可以先对括号内的运算取模,即 (a << bitLength(b)) + b ) % mod

代码


/**
 * @date 2026-02-28 9:18
 */
public class ConcatenatedBinary1680 {

    public int concatenatedBinary(int n) {
        int mod = 1000000007;
        long res = 0L;
        for (int i = 1; i <= n; i++) {
            int bl = 32 - Integer.numberOfLeadingZeros(i);
            res = ((res << bl) + i) % mod;
        }
        return (int) res;
    }

}

性能

1356.根据数字二进制下1的数目排序

目标

给你一个整数数组 arr 。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。

如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值大小升序排列。

请你返回排序后的数组。

示例 1:

输入:arr = [0,1,2,3,4,5,6,7,8]
输出:[0,1,2,4,8,3,5,6,7]
解释:[0] 是唯一一个有 0 个 1 的数。
[1,2,4,8] 都有 1 个 1 。
[3,5,6] 有 2 个 1 。
[7] 有 3 个 1 。
按照 1 的个数排序得到的结果数组为 [0,1,2,4,8,3,5,6,7]

示例 2:

输入:arr = [1024,512,256,128,64,32,16,8,4,2,1]
输出:[1,2,4,8,16,32,64,128,256,512,1024]
解释:数组中所有整数二进制下都只有 1 个 1 ,所以你需要按照数值大小将它们排序。

示例 3:

输入:arr = [10000,10000]
输出:[10000,10000]

示例 4:

输入:arr = [2,3,5,7,11,13,17,19]
输出:[2,3,5,17,7,11,13,19]

示例 5:

输入:arr = [10,100,1000,10000]
输出:[10,100,10000,1000]

说明:

  • 1 <= arr.length <= 500
  • 0 <= arr[i] <= 10^4

思路

将数组 arr 中的元素按照 bitCount 升序排列,如果相等根据元素值升序排列。

代码


/**
 * @date 2026-02-25 8:42
 */
public class SortByBits1356 {

    public int[] sortByBits(int[] arr) {
        return Arrays.stream(arr)
                .boxed()
                .sorted(Comparator.comparing(Integer::bitCount).thenComparing(Integer::intValue))
                .mapToInt(Integer::intValue)
                .toArray();
    }

}

性能

1461.检查一个字符串是否包含所有长度为K的二进制子串

目标

给你一个二进制字符串 s 和一个整数 k 。如果所有长度为 k 的二进制字符串都是 s 的子串,请返回 true ,否则请返回 false 。

示例 1:

输入:s = "00110110", k = 2
输出:true
解释:长度为 2 的二进制串包括 "00","01","10" 和 "11"。它们分别是 s 中下标为 0,1,3,2 开始的长度为 2 的子串。

示例 2:

输入:s = "0110", k = 1
输出:true
解释:长度为 1 的二进制串包括 "0" 和 "1",显然它们都是 s 的子串。

示例 3:

输入:s = "0110", k = 2
输出:false
解释:长度为 2 的二进制串 "00" 没有出现在 s 中。

说明:

  • 1 <= s.length <= 5 * 10^5
  • s[i] 不是 '0' 就是 '1'
  • 1 <= k <= 20

思路

检查一个二进制字符串是否包含所有长度为 k 的二进制子串。

长度为 k 的二进制子串的个数有 2^k 个,将子串视为二进制数字,使用长度为 k 的滑动窗口,记录窗口内子串表示的数字,判断是否所有数字都出现过即可。将左边界移出窗口需要将左边第一位置零 num = (num & (1 << (k - 1))) ^ num。先提取左边第一位,其余位均为 0,然后与 num 异或即可。

代码


/**
 * @date 2026-02-24 11:31
 */
public class HasAllCodes1461 {

    public boolean hasAllCodes(String s, int k) {
        if (k > 18) {
            return false;
        }
        int n = 1 << k;
        boolean[] visited = new boolean[n];
        int num = 0;
        char[] chars = s.toCharArray();
        int l = 0;
        for (int r = 0; r < chars.length; r++) {
            num = (num << 1) | (chars[r] - '0');
            if (r - l + 1 == k) {
                visited[num] = true;
                num = (num & (1 << (k - 1))) ^ num;
                l++;
            }
        }
        for (boolean b : visited) {
            if (!b) {
                return false;
            }
        }
        return true;
    }

}

性能

868.二进制间距

目标

给定一个正整数 n,找到并返回 n 的二进制表示中两个 相邻 1 之间的 最长距离 。如果不存在两个相邻的 1,返回 0 。

如果只有 0 将两个 1 分隔开(可能不存在 0 ),则认为这两个 1 彼此 相邻 。两个 1 之间的距离是它们的二进制表示中位置的绝对差。例如,"1001" 中的两个 1 的距离为 3 。

示例 1:

输入:n = 22
输出:2
解释:22 的二进制是 "10110" 。
在 22 的二进制表示中,有三个 1,组成两对相邻的 1 。
第一对相邻的 1 中,两个 1 之间的距离为 2 。
第二对相邻的 1 中,两个 1 之间的距离为 1 。
答案取两个距离之中最大的,也就是 2 。

示例 2:

输入:n = 8
输出:0
解释:8 的二进制是 "1000" 。
在 8 的二进制表示中没有相邻的两个 1,所以返回 0 。

示例 3:

输入:n = 5
输出:2
解释:5 的二进制是 "101" 。

说明:

  • 1 <= n <= 10^9

思路

判断正整数二进制表示中两个 1 之间 0 的个数,返回其长度 +1,如果不存在返回 0

根据题意模拟,先去掉右侧的尾零,然后记录 1 前面连续 0 的个数,取最大值即可。

代码


/**
 * @date 2026-02-24 15:03
 */
public class BinaryGap868 {

    public int binaryGap(int n) {
        int res = 0;
        while (n > 0) {
            while (n > 0 && (n & 1) == 0) {
                n >>= 1;
            }
            if (n == 0) {
                break;
            }
            n >>= 1;
            int cnt = 0;
            while (n > 0 && (n & 1) == 0) {
                n >>= 1;
                cnt++;
            }
            if (n > 0) {
                res = Math.max(res, cnt + 1);
            }
        }
        return res;
    }

}

性能

762.二进制表示中质数个计算置位

目标

给你两个整数 left 和 right ,在闭区间 [left, right] 范围内,统计并返回 计算置位位数为质数 的整数个数。

计算置位位数 就是二进制表示中 1 的个数。

例如, 21 的二进制表示 10101 有 3 个计算置位。

示例 1:

输入:left = 6, right = 10
输出:4
解释:
6 -> 110 (2 个计算置位,2 是质数)
7 -> 111 (3 个计算置位,3 是质数)
9 -> 1001 (2 个计算置位,2 是质数)
10-> 1010 (2 个计算置位,2 是质数)
共计 4 个计算置位为质数的数字。

示例 2:

输入:left = 10, right = 15
输出:5
解释:
10 -> 1010 (2 个计算置位, 2 是质数)
11 -> 1011 (3 个计算置位, 3 是质数)
12 -> 1100 (2 个计算置位, 2 是质数)
13 -> 1101 (3 个计算置位, 3 是质数)
14 -> 1110 (3 个计算置位, 3 是质数)
15 -> 1111 (4 个计算置位, 4 不是质数)
共计 5 个计算置位为质数的数字。

说明:

  • 1 <= left <= right <= 10^6
  • 0 <= right - left <= 10^4

思路

返回 [left, right] 范围内的数字中,二进制表示中 1 的个数为质数的数字个数。

1 ~ 10^6 的数位长度不超过 201 ~ 20 之间的质数有 2, 3, 5, 7, 11, 13, 17, 19。枚举每个数字计算其二进制表示中 1 的个数,判断是否是质数。

代码


/**
 * @date 2026-02-24 15:46
 */
public class CountPrimeSetBits762 {

    public static Set<Integer> primes;

    static {
        primes = new HashSet<>();
        primes.add(2);
        primes.add(3);
        primes.add(5);
        primes.add(7);
        primes.add(11);
        primes.add(13);
        primes.add(17);
        primes.add(19);
    }

    public int countPrimeSetBits(int left, int right) {
        int res = 0;
        for (int i = left; i <= right; i++) {
            int bitCount = Integer.bitCount(i);
            if (primes.contains(bitCount)) {
                res++;
            }
        }
        return res;
    }

}

性能

693.交替位二进制数

目标

给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。

示例 1:

输入:n = 5
输出:true
解释:5 的二进制表示是:101

示例 2:

输入:n = 7
输出:false
解释:7 的二进制表示是:111.

示例 3:

输入:n = 11
输出:false
解释:11 的二进制表示是:1011.

说明:

  • 1 <= n <= 2^31 - 1

思路

判断给定 num 的二进制表示是否是 01 交替出现的。

num 右移 1 位得到 mask,如果是 01 交替出现,那么 (mask & num) == 0 && (mask | num) == (1L << l) - 1,其中 lnum 的二进制长度。判断 x 是否全是 1,也可以使用 ((x + 1) & x) == 0

代码


/**
 * @date 2026-02-24 16:40
 */
public class HasAlternatingBits693 {

    public boolean hasAlternatingBits_v1(int n) {
        int mask = n >> 1;
        int or = mask | n;
        return (mask & n) == 0 && ((or + 1) & or) == 0;
    }

}

性能

401.二进制手表

目标

二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。每个 LED 代表一个 0 或 1,最低位在右侧。

  • 例如,下面的二进制手表读取 "4:51" 。

给你一个整数 turnedOn ,表示当前亮着的 LED 的数量,返回二进制手表可以表示的所有可能时间。你可以 按任意顺序 返回答案。

小时不会以零开头:

  • 例如,"01:00" 是无效的时间,正确的写法应该是 "1:00" 。

分钟必须由两位数组成,可能会以零开头:

  • 例如,"10:2" 是无效的时间,正确的写法应该是 "10:02" 。

示例 1:

输入:turnedOn = 1
输出:["0:01","0:02","0:04","0:08","0:16","0:32","1:00","2:00","4:00","8:00"]

示例 2:

输入:turnedOn = 9
输出:[]

说明:

  • 0 <= turnedOn <= 10

思路

有一个二进制手表,用 4 bit 表示小时, 6 bit 代表分钟。已知 bit 位为 1 的数量 turnedOn,返回所有可能的时间。

将数字按照置位数量分组,枚举不同的划分即可。

代码


/**
 * @date 2026-02-24 17:34
 */
public class ReadBinaryWatch401 {

    public List<String> readBinaryWatch(int turnedOn) {
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < 60; i++) {
            int bc = Integer.bitCount(i);
            map.putIfAbsent(bc, new ArrayList<>());
            map.get(bc).add(i);
        }
        List<String> res = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            List<Integer> hours = map.get(i);
            for (Integer h : hours) {
                if (h > 11) {
                    break;
                }
                List<Integer> minutes = map.get(turnedOn - i);
                if (minutes == null) {
                    continue;
                }
                for (Integer m : minutes) {
                    res.add(h + ":" + ((m < 10) ? "0" : "") + m);
                }
            }
        }
        return res;
    }
}

性能

190.颠倒二进制位

目标

颠倒给定的 32 位有符号整数的二进制位。

示例 1:

输入:n = 43261596
输出:964176192
解释:
整数       二进制
43261596  00000010100101000001111010011100
964176192 00111001011110000010100101000000

示例 2:

输入:n = 2147483644
输出:1073741822
解释:
整数        二进制
2147483644 01111111111111111111111111111100
1073741822 00111111111111111111111111111110

说明:

  • 0 <= n <= 2^31 - 2
  • n 为偶数

进阶: 如果多次调用这个函数,你将如何优化你的算法?

思路

颠倒 32 位有符号整数的二进制表示,返回其表示的数字。

题目限定 n 是正偶数,颠倒之后的最高位为 0,仍是非负数。模拟颠倒的过程,提前记录前导零的个数 lz,最后将结果左移 lz

代码


/**
 * @date 2024-06-08 22:28
 */
public class ReverseBits190 {

    public int reverseBits_new(int n) {
        int res = 0;
        int lz = Integer.numberOfLeadingZeros(n);
        while (n > 0) {
            int b = n & 1;
            res = (res << 1) | b;
            n >>= 1;
        }
        return res << lz;
    }

}

性能