【1342. 将数字变成 0 的操作次数】

Leetcode

1342. 将数字变成 0 的操作次数

给你一个非负整数 num ,请你返回将它变成 0 所需要的步数。 如果当前数字是偶数,你需要把它除以 2 ;否则,减去 1 。

示例 1:

输入:num = 14
输出:6
解释:
步骤 1) 14 是偶数,除以 2 得到 7 。
步骤 2) 7 是奇数,减 1 得到 6 。
步骤 3) 6 是偶数,除以 2 得到 3 。
步骤 4) 3 是奇数,减 1 得到 2 。
步骤 5) 2 是偶数,除以 2 得到 1 。
步骤 6) 1 是奇数,减 1 得到 0 。

示例 2:

输入:num = 8
输出:4
解释:
步骤 1) 8 是偶数,除以 2 得到 4 。
步骤 2) 4 是偶数,除以 2 得到 2 。
步骤 3) 2 是偶数,除以 2 得到 1 。
步骤 4) 1 是奇数,减 1 得到 0 。

示例 3:

输入:num = 123
输出:12

提示

0 <= num <= 10^6

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/number-of-steps-to-reduce-a-number-to-zero

class Solution {
    
    
public:
    int numberOfSteps(int num) {
    
    
        int ret = 0; // 初始化步骤数
        while (num) {
    
     // 当 num 不为 0 时
            ret += (num > 1 ? 1 : 0) + (num & 0x01); // 计算步骤数
            num >>= 1; // 将 num 右移一位
        }
        return ret; // 返回总步骤数
    }
};

这段C++代码是实现了一个名为Solution的类,并在其中定义了一个numberOfSteps的成员函数。这个函数接收一个整数num作为输入参数,然后计算将其变为0所需的操作次数。在每个操作中,根据num的奇偶性,可以执行以下两种操作之一:

  1. 如果num是偶数,则将其除以2;
  2. 如果num是奇数,则将其减去1。

现在我们来详细解释这段代码的关键部分:

ret += (num > 1 ? 1 : 0) + (num & 0x01);

这一行代码的作用是更新ret的值,ret变量用于记录执行的操作次数。这一行代码包含两部分,我们逐一解释。

第一部分:(num > 1 ? 1 : 0)

这是一个三元运算符的使用,它的作用是:如果num大于1,则返回1;否则返回0。这部分代码的意义在于,在执行操作的过程中,只要num大于1,那么下一步操作(除以2或者减去1)都会使num减小。所以在这种情况下,我们需要将操作次数ret加1。

第二部分:(num & 0x01)

这是一个按位与操作,将num与二进制数0x01进行按位与运算。这个操作的目的是检查num的最低有效位(LSB)是否为1,即判断num是否为奇数。如果num是奇数,那么(num & 0x01)的结果为1,表示我们需要先将奇数减1,再执行除以2的操作。如果num是偶数,那么(num & 0x01)的结果为0,表示我们只需要执行除以2的操作。

综上所述,这行代码的作用是:

  1. 判断num是否大于1,如果是,则需要执行一次操作,将ret加1;
  2. 判断num是否为奇数,如果是,则需要执行一次减1操作,将ret加1。

在这一行代码之后,num会右移一位(num >>= 1;),相当于将num除以2。然后继续循环,直到num变为0,此时ret的值就是所需的操作次数。

num=14的详细流程

现在我们来详细解析num = 14时的计算过程。首先,我们设定一个变量ret = 0来记录操作次数。

  1. 第一次循环:
    • num = 14(二进制表示为1110
    • num > 1,所以(num > 1 ? 1 : 0)的结果为1,ret加1。
    • num是偶数(最低有效位为0),所以(num & 0x01)的结果为0,ret不变。
    • ret = 0 + 1 = 1
    • 右移num(相当于除以2),得到num = 7(二进制表示为111)。
  2. 第二次循环:
    • num = 7(二进制表示为111
    • num > 1,所以(num > 1 ? 1 : 0)的结果为1,ret加1。
    • num是奇数(最低有效位为1),所以(num & 0x01)的结果为1,ret加1。
    • ret = 1 + 1 + 1 = 3
    • 右移num(相当于除以2),得到num = 3(二进制表示为11)。
  3. 第三次循环:
    • num = 3(二进制表示为11
    • num > 1,所以(num > 1 ? 1 : 0)的结果为1,ret加1。
    • num是奇数(最低有效位为1),所以(num & 0x01)的结果为1,ret加1。
    • ret = 3 + 1 + 1 = 5
    • 右移num(相当于除以2),得到num = 1(二进制表示为1)。
  4. 第四次循环:
    • num = 1(二进制表示为1
    • num <= 1,所以(num > 1 ? 1 : 0)的结果为0,ret不变。
    • num是奇数(最低有效位为1),所以(num & 0x01)的结果为1,ret加1。
    • ret = 5 + 1 = 6
    • 右移num(相当于除以2),得到num = 0

由于num变为0,循环结束,此时ret = 6。所以,将num = 14变为0需要执行6个操作。

num >>= 1和 num/2的区别

num >>= 1num /= 2这两个操作都会实现将num除以2的功能。但在计算过程和底层实现上,它们之间存在一定差别。

  1. 计算过程:
    • num >>= 1:这是一个右移位运算,它将num的二进制表示向右移动一位,即将每个位的值向右移动一位,高位补0。对于非负整数,右移一位相当于将原数除以2。
    • num /= 2:这是一个除法运算,它将num除以2,并将结果赋值给num
  2. 底层实现:
    • 位运算(如右移)在计算机硬件层面上执行得更快,因为它只需处理数值的二进制表示。而除法运算相对较慢,因为它需要进行更复杂的计算过程。
    • 通常情况下,编译器会自动优化除以2的操作,将其转换为更快的右移运算,但这取决于编译器的实现。
  3. 结果差异:
    • 对于非负整数,num >>= 1num /= 2的结果相同。
    • 对于负整数,由于负数在计算机中采用补码表示,右移操作可能导致不同的结果。在这种情况下,num >>= 1num /= 2可能产生不同的结果。

综上所述,num >>= 1num /= 2在处理非负整数时结果相同,但在处理负整数时可能不同。位运算通常更快,但在大多数情况下,这两种操作之间的性能差异可以忽略不计。

猜你喜欢

转载自blog.csdn.net/u013454780/article/details/130496794
今日推荐