位运算
1. 面试的题目很多是关于位运算的。位运算包括:与,或,异或,取反,左移,右移。
2.下面是几道位运算的经典题目。
2.1 判断一个数是不是2的密次方?
解析:一个数要是2的密次方,必然其二进制表示只有一位为1。所以题目转换为判读这个数是否只有一位为1??代码如下:
1 bool IsTwoPower(unsigned int x) 2 { return !(x & (x-1)); }
但是上述代码有一个问题,就是当x=0时,返回值也是true,显然0不是2的密次方。代码修改如下:
1 bool IsTwoPower(unsigned int x) 2 { return x && !(x & (x-1)); }
2.2 给定一个正整数,求出其二进制表示中1的个数?
解析:最容易想到的方法,用这个数与1相与,判断结果是否为1,然后这个数右移一位,直到这个数为0为止。时间复杂度为O(k),k为左边第一个1出现的位置。常用的并且高效的方法是上述方法的改变,时间复杂度为O(N),N为1的个数。代码如下:
1 int NumberOfOne1(unsigned int num) 2 { 3 int count=0; 4 while (num!=0) 5 { 6 if (num&1) 7 { 8 count++; 9 } 10 num=num>>1; 11 } 12 return count; 13 } 14 15 //上面1的改版,上面的算法对整数没问题,但是对负数 16 //会出现死循环,改成下面的形式 17 int ChangeNumberOfOne1(unsigned int num) 18 { 19 unsigned int flag=1; 20 int count=0; 21 while (flag) 22 { 23 if (flag & num) 24 { 25 count++; 26 } 27 flag=flag<<1; 28 } 29 return count; 30 } 31 32 //改进的算法,也是比较高效的算法 33 //每循环一次,num就会少一个1 34 int NumberOfOne2(unsigned int num) 35 { 36 int count=0; 37 while (num!=0) 38 { 39 count++; 40 num=num & (num-1); 41 } 42 return count; 43 }
2.3 给定一个unsigned int将其按二进制表示反转?
分析:目前出现最多的有两种算法。算法1:类似于数组的反转,每次交换最左边和最右边未交换的数。由于是二进制只有两种可能:要么是0,要么是1。两个位置的数同为0或1时,不需要交换。不同时,即异或结果为1时,才要交换。交换同样使用异或操作,原理:任何数(0或1)和1异或就是取反。算法2:使用的是类似于归并的交换算法。比如要交换:01101101。第一次将奇数位和偶数位交换:10011110,第二次将相邻每两位交换:01101011,第三步将相邻四位交换:10110110。
算法1代码如下:
1 typedef unsigned int uint; 2 uint swapBits(uint x, uint i, uint j) { 3 uint lo = ((x >> i) & 1); 4 uint hi = ((x >> j) & 1); 5 if (lo ^ hi) { 6 x ^= ((1U << i) | (1U << j)); 7 } 8 return x; 9 } 10 11 uint reverseXor(uint x) { 12 uint n = sizeof(x) * 8; 13 for (uint i = 0; i < n/2; i++) { 14 x = swapBits(x, i, n-i-1); 15 } 16 return x; 17 }
算法2代码如下:
1 uint reverseMask(uint x) { 2 assert(sizeof(x) == 4); // special case: only works for 4 bytes (32 bits). 3 x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1); 4 x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2); 5 x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4); 6 x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8); 7 x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16); 8 return x; 9 }
2.4 给定一个数组,这个数组中只有一个数出现1次,其余数均出现两次。求出这个数。
解析:使用异或操作。因为两个相同的数异或结果为0,任何数和0异或结果为其本身。代码如下:
1 int FindOne(int num[],int length) 2 { 3 assert(num && length>0); 4 int result=0; 5 for (int i=0;i<length;i++) 6 { 7 result^=num[i]; 8 } 9 return result; 10 }
2.5 上一题的变形。给定一个数组,这个数组中只有2个数出现1次,其余数均出现两次。求出这2个数。
解析:有两个数字只出现一次,显然不能像上一题一样直接使用异或运算。但是如果把数组分成两部分,把两个只出现一次的数分到两个部分,在每一个部分中只有一个数出现一次,就可以使用上一题的算法。
我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0,也就是说在这个结果数字的二进制表示中至少就有一位为1。我们在结果数字中找到第一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0。
现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。因此到此为止,所有的问题我们都已经解决。代码如下:
1 #include <iostream> 2 #include <cassert> 3 4 using namespace std; 5 6 //判断第indexBit是否为1 7 bool IsBit1(int num,unsigned int indexBit) 8 { 9 num=num>>indexBit; 10 return (num & 1); 11 } 12 13 //找到num中右边第一个1出现的位置 14 unsigned int FindFirstBit1(int num) 15 { 16 int indexBit=0; 17 while (((num & 1)==0) && (indexBit<32)) 18 { 19 num=num>>1; 20 ++indexBit; 21 } 22 return indexBit; 23 } 24 25 void FindNumsAppearOnce(int data[],int length,int& num1,int& num2) 26 { 27 if (length<2) 28 { 29 return; 30 } 31 int resultExclusiveOR=0; 32 for (int i=0;i<length;i++) 33 { 34 resultExclusiveOR^=data[i]; 35 } 36 unsigned int indexOf1=FindFirstBit1(resultExclusiveOR); 37 num1=num2=0; 38 for (int j=0;j<length;j++) 39 { 40 if (IsBit1(data[j],indexOf1)) 41 { 42 num1^=data[j]; 43 } 44 else 45 num2^=data[j]; 46 } 47 } 48 49 int main() 50 { 51 const int length=10; 52 int number[length]={2,3,2,12,12,1234,1234,2345,2345,9999}; 53 int num1=0; 54 int num2=0; 55 FindNumsAppearOnce(number,length,num1,num2); 56 cout<<num1<<" "<<num2<<endl; 57 }
2.6 下面的代码:
1 int f(int x,int y) 2 { 3 return (x&y)+((x^y)>>1); 4 }
的作用是取x和y的平均数:x&y是把相同的位除以2,(x^y)>>1是将不同的位除以2。相加之后就是取平均数的结果!!!
参考文章:
http://zhedahht.blog.163.com/blog/static/2541117420071128950682/
http://www.leetcode.com/category/bit-operations
http://blog.sina.com.cn/s/blog_63ce05ca0100u0ft.html
《编程之美》