位运算
- 输入一个整数,输出该数二进制表示中1的个数。附加题:判断一个数是否是2的幂。
- 一个整数数组里除了两个数字以外,其他数字都出现两次。请找出只出现一次的数字,要求时间复杂度O(n),空间复杂度O(1)。
- 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/即加减乘除四则运算符号。
1、思路:
常规方法:逐位和1做与运算,看是否为1。如果右移位,当符号位为1时,会陷入死循环(高位补1)。所以选择左移位,当标志位为0时说明所有位都移动完成。
巧妙方法:当n-1时,与n相比,最右边的1变为0,再右侧的0变为1。所以n&(n-1)不为0,则计数加1,有几个1就有几次循环。
判断2的幂:用二进制表示该数,如果是2的幂,则只有高位出现1,其他位都是0。
1 int NumberOf1_Solution1(int n) 2 { 3 int count = 0; 4 unsigned int flag = 1; 5 while(flag) 6 { 7 if(n & flag) 8 count ++; 9 10 flag = flag << 1; 11 } 12 13 return count; 14 } 15 16 int NumberOf1_Solution2(int n) 17 { 18 int count = 0; 19 20 while (n) 21 { 22 ++ count; 23 n = (n - 1) & n; 24 } 25 26 return count; 27 }
2、思路:
空间复杂度O(1)说明不能用hash。真经:异或自己得0,异或0得自己。首先,所有数组中的数字都异或一遍,出现两次的相互抵消,最后的结果是两个只出现1次的数字异或的结果。其次,最右边出现1的位说明两个数在该位上不同,则以该位为0或为1分隔成两个数组。最后,在新的两个数组上都异或一遍,得两个数字。
1 void FindNumsAppearOnce(int data[], int length, int* num1, int* num2) 2 { 3 if (data == NULL || length < 2) 4 return; 5 6 int resultExclusiveOR = 0; 7 for (int i = 0; i < length; ++ i) 8 resultExclusiveOR ^= data[i]; 9 10 unsigned int indexOf1 = FindFirstBitIs1(resultExclusiveOR); 11 12 *num1 = *num2 = 0; 13 for (int j = 0; j < length; ++ j) 14 { 15 if(IsBit1(data[j], indexOf1)) 16 *num1 ^= data[j]; 17 else 18 *num2 ^= data[j]; 19 } 20 } 21 22 // 找到num从右边数起第一个是1的位 23 unsigned int FindFirstBitIs1(int num) 24 { 25 int indexBit = 0; 26 while (((num & 1) == 0) && (indexBit < 8 * sizeof(int))) 27 { 28 num = num >> 1; 29 ++ indexBit; 30 } 31 32 return indexBit; 33 } 34 35 // 判断数字num的第indexBit位是不是1 36 bool IsBit1(int num, unsigned int indexBit) 37 { 38 num = num >> indexBit; 39 return (num & 1); 40 }
3、思路:
只能用位运算。模拟加法过程,先各个位上相加不考虑进位,再考虑进位,最后把进位加上。而0+0=0,1+1=0,0+1=1,1+0=1,即异或的结果。只有1+1=0才会产生进位,那么1&1=1,将1左移表示进位。
1 int Add(int num1, int num2) 2 { 3 int sum, carry; 4 do 5 { 6 sum = num1 ^ num2; 7 carry = (num1 & num2) << 1; 8 9 num1 = sum; 10 num2 = carry; 11 } 12 while(num2 != 0); 13 14 return num1; 15 }