遍历二进制详解
遍历二进制指的是对整数的二进制表示进行逐位操作或处理。它常用于位运算、算法设计以及特定场景下的优化(如权限检查、子集生成等)。以下详细讲解遍历二进制的多种方法及其应用。
1. 常见场景和需求
- 统计二进制中 1 的个数(如判断某数是 2 的幂)。
- 生成所有可能的二进制表示(如子集、组合、状态空间)。
- 逐位操作处理(如反转、提取、清除特定位)。
- 检查某个位的状态(如权限系统的标记位操作)。
2. 基本方法:逐位遍历
2.1 使用位移操作逐位遍历
通过移位操作逐位检查一个整数的每个位(从低位到高位),这是最基本的二进制遍历方法。
示例代码
public class BinaryTraversal {
public static void main(String[] args) {
int n = 23; // 二进制:10111
for (int i = 0; i < 32; i++) {
// 获取 n 的第 i 位
int bit = (n >> i) & 1; // 右移 i 位,然后取最低位
System.out.println("第 " + i + " 位: " + bit);
}
}
}
输出
第 0 位: 1
第 1 位: 1
第 2 位: 1
第 3 位: 0
第 4 位: 1
第 5 位: 0
...
2.2 从高位到低位遍历
将 1
逐步左移,与目标数进行按位与操作,检查每一位是否为 1。
示例代码
public class BinaryTraversal {
public static void main(String[] args) {
int n = 23; // 二进制:10111
for (int i = 31; i >= 0; i--) {
int bit = (n & (1 << i)) == 0 ? 0 : 1; // 检查第 i 位是否为 1
System.out.println("第 " + i + " 位: " + bit);
}
}
}
3. 统计二进制中 1 的个数
3.1 使用逐位遍历
示例代码
public class CountOnes {
public static void main(String[] args) {
int n = 23; // 二进制:10111
int count = 0;
while (n != 0) {
count += n & 1; // 检查最低位是否为 1
n >>= 1; // 右移一位
}
System.out.println("二进制中 1 的个数: " + count);
}
}
3.2 使用优化方法(n & (n - 1))
n & (n - 1)
会将 n
的最低位 1 清零,因此只需执行这个操作直到 n == 0
,即可统计 1 的个数。
示例代码
public class CountOnes {
public static void main(String[] args) {
int n = 23; // 二进制:10111
int count = 0;
while (n != 0) {
n &= (n - 1); // 清除最低位的 1
count++;
}
System.out.println("二进制中 1 的个数: " + count);
}
}
时间复杂度
- 每次清除一个 1,因此时间复杂度为 O ( k ) O(k) O(k),其中 k k k 是二进制中 1 的个数。
4. 生成所有可能的二进制表示
4.1 枚举所有长度为 n 的二进制数
对于长度为 n
的二进制,可以通过枚举 [0, 2^n - 1]
的整数来生成。
示例代码
public class GenerateBinary {
public static void main(String[] args) {
int n = 3; // 二进制长度为 3
int limit = 1 << n; // 2^n 个可能
for (int i = 0; i < limit; i++) {
System.out.println(Integer.toBinaryString(i));
}
}
}
输出
000
001
010
011
100
101
110
111
4.2 生成某集合的所有子集
集合的子集可以用二进制表示,每个二进制位表示一个元素是否被选中。
示例代码
import java.util.*;
public class Subsets {
public static void main(String[] args) {
String[] elements = {"A", "B", "C"}; // 集合元素
int n = elements.length;
for (int i = 0; i < (1 << n); i++) {
List<String> subset = new ArrayList<>();
for (int j = 0; j < n; j++) {
if ((i & (1 << j)) != 0) { // 检查第 j 位是否为 1
subset.add(elements[j]);
}
}
System.out.println(subset);
}
}
}
输出
[]
[A]
[B]
[A, B]
[C]
[A, C]
[B, C]
[A, B, C]
5. 高级应用
5.1 检查是否是 2 的幂
- 原理:2 的幂次方数的二进制表示中只有 1 个 1。
- 条件:
n > 0 && (n & (n - 1)) == 0
示例代码
public class PowerOfTwo {
public static boolean isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
public static void main(String[] args) {
System.out.println(isPowerOfTwo(8)); // true
System.out.println(isPowerOfTwo(10)); // false
}
}
5.2 提取最低位的 1
- 操作:
n & (-n)
- 作用:将二进制中最低位的 1 提取出来,其余位清零。
示例代码
public class LowestBit {
public static void main(String[] args) {
int n = 18; // 二进制:10010
int lowestBit = n & -n; // 提取最低位的 1
System.out.println(lowestBit); // 输出:2(即二进制 0010)
}
}
5.3 翻转二进制
将整数的二进制位顺序反转。
示例代码
public class ReverseBinary {
public static int reverseBits(int n) {
int result = 0;
for (int i = 0; i < 32; i++) {
result <<= 1; // 左移一位腾出位置
result |= (n & 1); // 将 n 的最低位追加到 result
n >>= 1; // 右移 n
}
return result;
}
public static void main(String[] args) {
int n = 5; // 二进制:101
System.out.println(reverseBits(n)); // 输出:2684354560(即二进制 101...000)
}
}
6. 总结
常见操作和用途
操作 | 方法 | 用途 |
---|---|---|
遍历二进制的每一位 | 移位操作或按位与 | 位检查、逐位处理 |
统计 1 的个数 | n & (n - 1) 或移位累加 | 判断奇偶、统计状态 |
生成二进制表示 | 枚举 [0, 2^n-1] | 子集生成、状态空间 |
检查最低位是否为 1 | n & 1 | 奇偶判断、低位检查 |
清除最低位的 1 | n & (n - 1) | 状态转移、减少进位 |
提取最低位的 1 | n & -n | 位标记操作 |
检查是否为 2 的幂 | (n > 0) && (n & (n - 1)) == 0 | 判断特定属性 |
翻 | ||
转二进制位顺序 | 逐位提取并左移 | 图像处理、位图算法 |
掌握二进制遍历和操作技巧是理解位运算以及实现高效算法的基础。