Easy
题目描述
数字 x 的一次位翻转操作是指选择 x 二进制表示中的任意一位,将其从 0 变为 1 或从 1 变为 0。
例如,对于 x = 7,其二进制表示是 111,我们可以选择任意一位(包括未显示的前导零)并翻转它。我们可以翻转右边第一位得到 110,翻转右边第二位得到 101,翻转右边第五位(前导零)得到 10111 等。
给定两个整数 start 和 goal,返回将 start 转换为 goal 所需的最少位翻转次数。
示例 1:
输入: start = 10, goal = 7
输出: 3
解释: 10 和 7 的二进制表示分别为 1010 和 0111。我们可以通过 3 步将 10 转换为 7:
- 翻转右边第一位: 1010 -> 1011
- 翻转右边第三位: 1011 -> 1111
- 翻转右边第四位: 1111 -> 0111
可以证明我们无法在少于 3 步的情况下将 10 转换为 7。因此,我们返回 3。
示例 2:
输入: start = 3, goal = 4
输出: 3
解释: 3 和 4 的二进制表示分别为 011 和 100。我们可以通过 3 步将 3 转换为 4:
- 翻转右边第一位: 011 -> 010
- 翻转右边第二位: 010 -> 000
- 翻转右边第三位: 000 -> 100
可以证明我们无法在少于 3 步的情况下将 3 转换为 4。因此,我们返回 3。
约束条件:
- 0 <= start, goal <= 10^9
注意: 这道题与第 461 题:汉明距离相同。
提示:
- 如果 start 和 goal 中某一位的值不同,则需要翻转该位。
- 考虑使用 XOR 操作来确定哪些位需要翻转。
解题思路
这道题的核心思路是找出两个数字在二进制表示中有多少位不同,每个不同的位都需要进行一次翻转操作。
分析过程
我们可以通过异或(XOR)操作来解决这个问题。异或操作有一个重要性质:相同的位得到0,不同的位得到1。因此,start ^ goal 的结果中,1的个数就是需要翻转的位数。
例如:
- start = 10 (1010), goal = 7 (0111)
- 1010 XOR 0111 = 1101
- 1101中有3个1,所以需要3次翻转
解法思路
方法一:使用内置函数 大多数编程语言都提供了计算二进制中1的个数的内置函数,这是最简洁的解法。
方法二:位运算技巧
使用 n & (n-1) 的技巧可以消除最低位的1,重复此操作直到数字变为0,操作次数就是1的个数。
方法三:逐位检查 通过不断右移和检查最低位来统计1的个数。
推荐使用方法一,因为它最简洁且效率很高。
代码实现
class Solution {
public:
int minBitFlips(int start, int goal) {
return __builtin_popcount(start ^ goal);
}
};
class Solution:
def minBitFlips(self, start: int, goal: int) -> int:
return (start ^ goal).bit_count()
public class Solution {
public int MinBitFlips(int start, int goal) {
return System.Numerics.BitOperations.PopCount((uint)(start ^ goal));
}
}
var minBitFlips = function(start, goal) {
let xor = start ^ goal;
let count = 0;
while (xor !== 0) {
count += xor & 1;
xor >>>= 1;
}
return count;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(1) | 使用内置函数计算位数,对于32位整数是常数时间 |
| 空间复杂度 | O(1) | 只使用常数额外空间 |