剑指offer 15 二进制中1的个数

本文深入探讨了位运算的基本操作,如左移、右移及其在整数处理中的应用,并通过实例介绍了如何利用位运算解决特定问题,如计算二进制中1的个数、判断整数是否为2的幂等。

位运算:

左移运算符m<<n表示把m左移n位。在左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0

例如:

00001010<<2=00101000

10001010<<3=01010000

右移运算符与左移类似,但右移时处理最左边位时稍微复杂:

1、如果数字是一个无符号数值,则用0填补最左边的n位

2、如果数字是一个有符号数值,则用数字的符号填补最左边的n位

00001010>>2=00000010    正数,用0填补最左边的n位

10001010>>3=11110001    负数,用1填补最左边的n位

题目:

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

class Solution {
public:
     int  NumberOf1(int n) {
         int count=0;
         int flag=1;
   while(flag)
   {
       if(n&flag)
       count++;
       flag=flag<<1;
   }
       return count;
    }
};

问题:把整数右移一位和把整数除以2在数学上是等价的,那上面的代码中,可以把右移运算换成除以2吗?

答案:不可以,除法的运算效率比移位运算要低太多,在实际编程中尽可能使用移位运算符代替乘除法

还有避免程序陷入死循环,我们一般不直接移动整数,因为负数右移会陷入死循环。

我们可以采用使1移位,判断当前位是否是1,嗯,这真的是一个好方法。

下面还有更高效的方法

思路:

我们发现将一个整数减去1,再和原来的整数做位&运算,会把该整数最右边的1变成0。

因此一个整数的二进制中有多少1,它就可以做多少次这样的运算。

很多二进制问题都可以用这种方法计算。

举例:

1、1100,减一后为1011,1011&1100=1000

2、1000,减一后为0111,0111&1000=0000

代码:
 

class Solution {
public:
     int  NumberOf1(int n) {
         int count=0;
          
   while(n)
   {
       count++;
       n=(n-1)&n;
   }
       return count;
    }
};

相关题目:

1、用一条语句判断一个整数是不是2的整数次方。

 一个整数如果是2的整数次方,那么他的二进制表示中有且只有一位是1,而其他所有位都是0.

根据前面的分析,把这个整数减去1之后再和它自己做与运算,这个整数中唯一的1就会变成0

2、输入两个整数 m和n, 计算需要改变m的二进制表示中的多少位才能得到n。

比如10的二进制表示为1010,13的二进制表示是1101,则需要改变1010中的3位才能得到1101.

思路:

第一步:求这两个数的异或

第二步:统计异或结果中1的位数,就又回到了前面的解法。

题目 二:
lintcode 给出两个整数a和b, 求他们的和, 但不能使用 + 等数学运算符。

思路:
考虑一个普通的加法计算:5+17=22

在十进制加法中可以分为如下3步进行:
    1. 忽略进位,只做对应各位数字相加,得到12(个位上5+7=12,忽略进位,结果2);
    2. 记录进位,上一步计算中只有个位数字相加有进位1,进位值为10;
    3. 按照第1步中的方法将进位值与第1步结果相加,得到最终结果22。

下面考虑二进制数的情况(5=101,17=10001):
仍然分3步:
    1. 忽略进位,对应各位数字相加,得到10100;
    2. 记录进位,本例中只有最后一位相加时产生进位1,进位值为10(二进制);
    3. 按照第1步中的方法将进位值与第1步结果相加,得到最终结果10110,正好是十进制数22的二进制表示。

接下来把上述二进制加法3步计算法用位运算替换:
    第1步(忽略进位):0+0=0,0+1=1,1+0=0,1+1=0,典型的异或运算。
    第2步:很明显,只有1+1会向前产生进位1,相对于这一数位的进位值为10,而10=(1&1)<<1。
    第3步:将第1步和第2步得到的结果相加,其实又是在重复上述2步,直到不再产生进位为止。

源代码:

class Solution {  
    /* 
     * param a: The first integer 
     * param b: The second integer 
     * return: The sum of a and b 
     */  
    public int aplusb(int a, int b) {  
        // write your code here, try to do it without arithmetic operators.  
        if(a==0) return b;  
        if(b==0) return a;  
        int sum,i;  
        i=a^b;  //异或等价于两个数的相加,不考虑进位
        sum=(a&b)<<1;  //sum为进位的值
        return aplusb(sum,i);  //当进位sum为0时,返回i
    }  
};  

参考:http://m.blog.youkuaiyun.com/article/details?id=50686724

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值