Hard
题目描述
给定一个整数 n,你必须使用以下操作任意次数将其变为 0:
- 改变
n的二进制表示中最右边(第 0 位)的位。 - 如果第
(i-1)位为1且第(i-2)到第0位都为0,则可以改变第i位。
返回将 n 变为 0 的最少操作次数。
示例 1:
输入:n = 3
输出:2
解释:3 的二进制表示是 "11"。
"11" -> "01" 使用第 2 种操作,因为第 0 位是 1。
"01" -> "00" 使用第 1 种操作。
示例 2:
输入:n = 6
输出:4
解释:6 的二进制表示是 "110"。
"110" -> "010" 使用第 2 种操作,因为第 1 位是 1 且第 0 位是 0。
"010" -> "011" 使用第 1 种操作。
"011" -> "001" 使用第 2 种操作,因为第 0 位是 1。
"001" -> "000" 使用第 1 种操作。
约束条件:
0 <= n <= 10^9
提示:
- 将 n 转换为零的最快方法是从最左边开始删除所有设置位。尝试一些简单的例子来学习删除一个设置位需要多少步骤的规律。
- 首先考虑 n=2^k 的情况,然后解决所有 n。
解题思路
这道题的核心是理解格雷码(Gray Code)的性质。根据操作规则,我们需要找到一个转换序列将 n 变为 0。
关键观察:
对于
n = 2^k(只有最高位为1),设f(2^k)表示将其变为0的最少操作数,可以推导出:f(2^k) = 2^(k+1) - 1这个问题本质上是格雷码的逆向操作。格雷码有一个重要性质:相邻两个数字只有一位不同。
对于任意数字 n,我们可以使用递推公式:
- 找到 n 的最高位 k
f(n) = f(2^k) - f(n ^ 2^k)
这个公式的含义是:要清除最高位,我们需要
f(2^k)步操作,但由于还有其他位,我们需要减去处理剩余位的操作数。
算法步骤:
- 如果 n = 0,返回 0
- 找到 n 的最高位位置 k
- 递归计算:
f(2^k) - f(n ^ 2^k) - 其中
f(2^k) = 2^(k+1) - 1
这种方法利用了格雷码的性质,通过递推关系高效求解。
代码实现
class Solution {
public:
int minimumOneBitOperations(int n) {
if (n == 0) return 0;
// 找到最高位
int k = 0;
while ((1 << (k + 1)) <= n) k++;
// f(2^k) = 2^(k+1) - 1
// f(n) = f(2^k) - f(n ^ 2^k)
return (1 << (k + 1)) - 1 - minimumOneBitOperations(n ^ (1 << k));
}
};
class Solution:
def minimumOneBitOperations(self, n: int) -> int:
if n == 0:
return 0
# 找到最高位
k = 0
while (1 << (k + 1)) <= n:
k += 1
# f(2^k) = 2^(k+1) - 1
# f(n) = f(2^k) - f(n ^ 2^k)
return (1 << (k + 1)) - 1 - self.minimumOneBitOperations(n ^ (1 << k))
public class Solution {
public int MinimumOneBitOperations(int n) {
if (n == 0) return 0;
// 找到最高位
int k = 0;
while ((1 << (k + 1)) <= n) k++;
// f(2^k) = 2^(k+1) - 1
// f(n) = f(2^k) - f(n ^ 2^k)
return (1 << (k + 1)) - 1 - MinimumOneBitOperations(n ^ (1 << k));
}
}
/**
* @param {number} n
* @return {number}
*/
var minimumOneBitOperations = function(n) {
if (n === 0) return 0;
let k = 0;
let temp = n;
while (temp > 0) {
k++;
temp >>= 1;
}
k--;
return (1 << (k + 1)) - 1 - minimumOneBitOperations(n ^ (1 << k));
};
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(log n) | 递归深度等于 n 的二进制位数 |
| 空间复杂度 | O(log n) | 递归调用栈的深度 |