位运算
引子:二进制中1的个数
请实现一个函数,输入一个整数,输出该数二进制表示中的个数。例如把9表示成二进制是1001,有2位是1.因此如果输入9,该函数输出2。
此题容易进入一个误区:误认为输入的数一定是正数,于是我很快写出这样的代码:
1 int num(int n) 2 { 3 int count=0; 4 while(n) 5 { 6 if(n&1) count++; 7 n=n>>1; 8 } 9 return count; 10 }
看完书,发现自己落入了作者或者说面试官所设的圈套。如果输入的是-9,那么程序就会陷入死循环,因为移位前是个负数,符号位为1,移位后还要保证是负数,所以最高位还是设为1,一直做右移运算,最终变成0xFFFFFFFF,一直循环下去。
好吧,既然有此题做为做引子,那么本文就好好学习一下编程中的位运算。
其实位运算加起来也就5中运算:与、或、异或、左移和右移。
(1)与、或和异或
与(&) | 0&0=0 | 1&0=0 | 0&1=0 | 1&1=1 |
或(|) | 0|0=0 | 1|0=1 | 0|1=1 | 1|1=1 |
异或(^) | 0^0=0 | 1^0=1 | 0^1=1 | 1^1=0 |
(2)左移和右移
m<<n:就是把m左移n位,最左边n位丢弃,最右边补n位0;
00001010<<2=00101000
10001010<<3=01010000
m>>n:分为两种情况,有符号数和无符号数。
无符号数:m右移n位,最右边n位丢弃,最左边补n位0;
0001010>>2=00000010
有符号数:m右移n位,最右边n位丢弃,最左边补n位1;如下面的8位有符号数右移:
10001010>>3=1110001
好了,那么就找一些位运算题目做个练习。(13.09.04 update )
题目:求数组中的唯一重复元素
一个数组中含有1001个元素,存放了1,2,3...,1000和一个重复数字。只有唯一一个数是重复的,其他均只出现一次。要求设计算法找出这个数字。要求:每个数组元素只能方位一次,不能使用辅助存储空间。
简单的方法是先求出1-1000的和sum1,然后遍历数组求到数组所有元素之和sum2,相减即为重复的数。这个方法存在的问题是,若是数组很大,那么求和可能会溢出。
这里呢,我们用位运算怎么做呢?我在《剑指offer》博文中写过“数组中只出现一次的数字”一题,用的就是异或运算求解,有兴趣的读者可以顺便看一下。本题思路一样,利用相同的数异或后为0这一思路,求得最后那个重复的数。
1 int main() 2 { 3 int a[]={1,2,3,4,5,6,3}; 4 5 int t=0; 6 int length =7; 7 8 for(int i=0;i<length;i++) 9 { 10 t^=i; 11 t^=a[i]; 12 } 13 cout<<t<<endl;//输出t为3 14 return 0; 15 }
参考:
1.何海涛《剑指offer》