【剑指offer】二进制中1的个数(位运算)

题目:请实现一个函数,输入一个整数,输出该数的二进制表示中1的个数。例如,把9表示成二进制是1001,有2位是1.因此,如果输入9,则该函数输出2.

解法一(可能引起死循环)

int NumberOf1(int n){
    
    
    int count = 0;
    while(n){
    
    
        if(n & 1)
            count++;
        n = n >> 1;
    }
    return count;
}

上面的函数如果输入一个负数,比如0x80000000,则右移时,并不是简单地把最高位的1移到第二位变成0x40000000,而是0xC0000000,右移负数最高位会设为1.如果一直做右移运算,那最终这个数字会变成0xFFFFFFF而陷入死循环。

解法二(常规解法)

int NumberOf1(int n){
    
    
    int count = 0;
    unsigned int flag = 1;
    while(flag){
    
    
        if(n & flag)
            count++;
        flag = flag << 1;
    }
    return count;
}

不移动输入数字n,移动1。首先n和1做与运算,判断n的最低位是不是1,接着把1左移一位得到10,再和n做与运算,判断n的次低位是不是1…知道1左移到溢出。这种解法,循环的次数等于整数二进制的位数,32位的整数需要循环32次。下面再介绍一种算法,整数中有几个1就只要循环几次。

解法三

int NumberOf1(int n){
    
    
    int count = 0;
    while(n){
    
    
        count++;
        n = (n - 1) & n;//这样能让n最右边的1变成了0
    }
    return count;
}

为什么要(n - 1) & n:因为一个非零整数和它减一的结果做位与运算,相当于把它最右边的1变成0.以1100为例,它减1的结果是1011。把1100和1011做位与运算,得到的结果是1000,也就是把1100最右边的1变成0的结果。
如果抽象地举例,对于一个非零整数,如果最右边的一位是1,那么减1,最后一位变成0而其他所有位都保持不变。接下来假设最后一位不是1.如果该整数的二进制表示中最右边的1位于第m位,那么减1时,整数中第m位之前的所有位都保持不变,第m位变成0,第m位之后的所有位变成1。也就是从第m位开始往后的位都与原来的数相反。所以一个数和它减1的结果做与运算,相当于把它最右边的1给变成了0。这个整数有多少个1,就可以进行多少次这样的操作。

猜你喜欢

转载自blog.csdn.net/qq_41731507/article/details/114970118