引发学习位运算的原因是在网上浏览到了几道题:
第一题:
有一个给定的数组,其中仅有一个元素出现了一次,而其他的元素出现了两次。问如何找出这个仅出现一次的元素呢?
解:
其实看到这一题的第一反应是利用数组下标法。但是它的空间复杂度是O(n),是时间复杂度是O(n).
但是我们可不可再优化以下,尽量降低它的复杂度?
紧接着,又想到了利用哈希表存储每个元素出现的次数,当一个元素出现两次时,就从表中删除,最后只有一个元素。此举可以降低空间复杂度,但是并不是最优的选择。
而推荐
的解法就是利用异或运算。
举例说明:
1,2,1,2,5
运用异或后,1^2^1^2^=1^1^2^2^5。so根据异或的性质,最后0^5为5。
int find(int arr[100]){
int temp=arr[0];
for(int i=0;i<100;i++){
temp=temp^arr[i];
}
return temp;//直接返回了答案!
}
第二题
判断一个数是不是2的幂次
解:
我们观察是二的幂次数的二进制形式可以发现,其只有一位为1,其余位均为0.
那么我们可以根据这个性质来进行分析。
我们可以用到x&(x-1)这一操作。当最后的结果是0时,说明只有一位是1,说明其是二的幂次。
我们下边进行举例说明:如该数是1000,则其减1后的结果为0111,进行与运算后的结果为0;如果这个数是10100,减一后的结果是10011,进行与运算后的结果是1。
第三题:
找出不大于n的最大的2的幂指数;
解:
按二进制的形式来看,如10100,结果应该是最左边的非0位,故为4。所以我们得找出一种操作,是得10100000变为11111111,然后加1,得到了100000000,然后100000000>>1=10000000。
接下来,重点是变为全1的操作。假设这个数是8位的,而我们一定可以知道第k位一定是1((从左至右数的第一个非0位)。我们接下来让其右移一位,并进行或运算,得到的结果为第k位,第k+1位,一定是1。紧接着我们再进行右移两位第,则或运算后,第k,k+1,k+2,k+3位均为一定为1。依次类推进行右移四位,则可以保证这八位一定全为1.
x|x>>1;
x|x>>2;
x|x>>4;
之后再进行减一后移位操作。
int find(int x){
x|x>>1;
x|x>>2;
x|x>>4;
return (x+1)>>1;
}