Easy

题目描述

给你两个正整数 nk

你可以选择 n 的二进制表示中任意一个等于 1 的位,并将其改为 0

返回使 n 等于 k 所需的更改次数。如果无法实现,返回 -1

示例 1:

输入:n = 13, k = 4
输出:2
解释:
最初,n 和 k 的二进制表示分别为 n = (1101)2 和 k = (0100)2。
我们可以改变 n 的第一位和第四位。结果整数为 n = (0100)2 = k。

示例 2:

输入:n = 21, k = 21
输出:0
解释:
n 和 k 已经相等,所以不需要更改。

示例 3:

输入:n = 14, k = 13
输出:-1
解释:
无法使 n 等于 k。

约束条件:

  • 1 <= n, k <= 10^6

提示:

  • 找到 nk 的二进制表示。
  • 任何在 n 中等于 1 而在 k 中等于 0 的位都需要更改。

解题思路

这是一道位操作题目,核心思想是分析 n 和 k 的二进制表示。

分析题意:

  • 我们只能将 n 中的 1 改为 0,不能将 0 改为 1
  • 要使 n 等于 k,需要分析每一位的情况

关键观察:

  1. 如果 k 的某一位是 1,而 n 的对应位是 0,那么无法实现转换(因为我们不能把 0 改成 1)
  2. 如果 n 的某一位是 1,而 k 的对应位是 0,那么需要进行一次修改
  3. 其他情况(都是 0 或都是 1)不需要修改

解法思路:

  • 首先检查是否可能:k 的每一位为 1 的地方,n 的对应位也必须为 1
  • 如果可能,计算需要修改的位数:n 为 1 且 k 为 0 的位数

位运算技巧:

  • 使用 (k & n) == k 检查是否可能转换
  • 使用 n ^ k 得到不同的位,然后与 n 进行 AND 操作得到需要修改的位

代码实现

class Solution {
public:
    int minChanges(int n, int k) {
        // 如果k中为1的位,n中对应位不是1,则无法转换
        if ((k & n) != k) {
            return -1;
        }
        
        // 计算需要从1改为0的位数
        return __builtin_popcount(n ^ k);
    }
};
class Solution:
    def minChanges(self, n: int, k: int) -> int:
        # 如果k中为1的位,n中对应位不是1,则无法转换
        if (k & n) != k:
            return -1
        
        # 计算需要从1改为0的位数
        return bin(n ^ k).count('1')
public class Solution {
    public int MinChanges(int n, int k) {
        // 如果k中为1的位,n中对应位不是1,则无法转换
        if ((k & n) != k) {
            return -1;
        }
        
        // 计算需要从1改为0的位数
        int diff = n ^ k;
        int count = 0;
        while (diff > 0) {
            count += diff & 1;
            diff >>= 1;
        }
        return count;
    }
}
var minChanges = function(n, k) {
    // 如果k中为1的位,n中对应位不是1,则无法转换
    if ((k & n) !== k) {
        return -1;
    }
    
    // 计算需要从1改为0的位数
    let diff = n ^ k;
    let count = 0;
    while (diff > 0) {
        count += diff & 1;
        diff >>= 1;
    }
    return count;
};

复杂度分析

复杂度类型复杂度说明
时间复杂度O(log max(n,k))需要检查最多 log(max(n,k)) 位
空间复杂度O(1)只使用常数额外空间