LeetCode 693. 交替位二进制数
题目描述
给定一个正整数,检查它的二进制表示是否总是 0 和 1 交替出现:换句话说,就是二进制表示中相邻两位的数字永远不同。
示例 1
输入:n = 5
输出:true
解释:5 的二进制是 101,相邻位均不同。
示例 2
输入:n = 7
输出:false
解释:7 的二进制是 111,相邻位有相同(11 出现)。
示例 3
输入:n = 11
输出:false
解释:11 的二进制是 1011,最后两位 11 相同。
解法一:逐位检查(位运算)
思路
一个简单直观的方法是:从最低位开始,依次检查每一对相邻的二进制位是否相同。如果发现任何一对相邻位相同,则返回 false;如果所有相邻位都不同,则返回 true。
本题解提供的代码正是基于这种思想,但巧妙地利用了位运算来一次性检查最低两位是否相同,避免了逐位比较的繁琐。
代码
cpp
class Solution {
public:
bool hasAlternatingBits(int n) {
while (n > 1) { // 至少还有两位需要检查
// 检查最低两位是否都是 0 或都是 1
if (n == (n | 3) || (n & 3) == 0)
return false; // 出现相同相邻位,直接返回 false
n >>= 1; // 右移一位,准备检查下一对
}
return true; // 所有相邻位都不同,返回 true
}
};
代码解读
n > 1:当n大于 1 时,其二进制表示至少有两个二进制位,需要进行检查。如果n为 0 或 1,显然没有相邻位,直接返回true。- 数字
3的妙用 :3的二进制是11,通过n | 3可以将n的最低两位强制设为 1,而n & 3则可以提取n的最低两位。 - 检查最低两位是否相同 :
n == (n | 3):如果等式成立,说明n的最低两位原本就都是 1(因为或运算不会改变已经是 1 的位)。这意味着最低两位是11,相同。(n & 3) == 0:如果等式成立,说明n的最低两位都是 0,即00,也相同。- 只要以上两个条件之一成立,就说明最低两位相同,不满足交替要求,立即返回
false。
n >>= 1:当前最低两位检查完毕后,将n右移一位,原来的次低位变成新的最低位,下一轮循环就可以检查原第 2 位和第 3 位。如此反复,直到所有相邻位都被检查。
示例模拟
以 n = 5(二进制 101)为例:
- 初始
n = 5,n > 1成立。- 最低两位:
01,既不是00也不是11,条件不通过。 n >>= 1得2(二进制10)。
- 最低两位:
n = 2,n > 1成立。- 最低两位:
10,不是相同位,条件不通过。 n >>= 1得1,循环结束。
- 最低两位:
- 返回
true。
以 n = 7(二进制 111)为例:
n = 7,最低两位11,满足n == (n | 3)(7 | 3 = 7),直接返回false。
以 n = 4(二进制 100)为例:
n = 4,最低两位00,满足(n & 3) == 0,返回false。
复杂度分析
- 时间复杂度 :
O(log n),即二进制位数。每次循环右移一位,最多检查二进制位数次。 - 空间复杂度 :
O(1),只使用了常数个变量。
解法二:转换为字符串(直观)
另一种易于理解的方法是先将整数转换为二进制字符串,然后遍历检查相邻字符是否相等。这种方法虽然直观,但效率稍低,且需要额外空间。
cpp
class Solution {
public:
bool hasAlternatingBits(int n) {
string bits = "";
while (n) {
bits = to_string(n & 1) + bits; // 从高位构建字符串
n >>= 1;
}
for (int i = 1; i < bits.size(); ++i) {
if (bits[i] == bits[i - 1]) return false;
}
return true;
}
};
解法三:利用异或性质(进阶)
一个经典的位运算技巧:对于一个交替位二进制数 n,将其右移一位得到 n >> 1,两者进行异或运算,结果应该是一个全 1 的数(即所有位都是 1)。例如:
n = 5(101),n >> 1 = 10(010),异或得111,全 1。- 再检查这个结果加 1 后是否为 2 的幂:
(a & (a + 1)) == 0(其中a = n ^ (n >> 1))。
这种方法只需 O(1) 时间,非常简洁。
cpp
class Solution {
public:
bool hasAlternatingBits(int n) {
long a = n ^ (n >> 1); // 注意用 long 防止溢出
return (a & (a + 1)) == 0;
}
};
总结
本题主要考察对二进制位操作的理解。解法一通过逐对检查相邻位,思路清晰且空间效率高;解法二更直观但需要额外空间;解法三利用异或特性,代码最简洁,是面试中的优选方案。实际解题时可根据需要选择合适的方法。