Algorithm:位运算的这些小技巧你知道吗?
位运算的巧妙之处
算法中,位运算可以巧妙运用在一下几个方面:
1、判断奇偶数 => x&1
2、判断数x中第k ( 从右至左 ) 位是1还是0
法1:( x >> ( k - 1 ) ) & 1
法2:x & ( 1 << ( k - 1 ) )
3、交换两个整数变量 a , b 的值
a = a ^ b;
b = b ^ a;
a = a ^ b;
这里为什么能这样做,在后面的异或运算中会说明;
4、不用判断语句,求整数绝对值
return (value ^ (value >> 31))-(value >>31)
同样在异或运算中说明;
应用
二进制中1的个数
描述:实现一个函数,输入一个正整数,输出该数二进制表示中1的个数。
例:9的二进制表示为1001,有2位是1;
思路:循环运用判断x的第k位是否为1的方法;
int count=0;
while(value)
{
count += value & 1;
value = value >> 1;
}
return count;
方法二:
&运算有这样一个性质:a = ( a - 1 ) & a ; 这样a就能消除最低位的一个1;
思路:利用这一性质,我们可以每次将value-1,然后与自己&,能做多少次这样的操作就说明有多少个1;
int count=0;
while(value)
{
value = (value - 1) & value;
count++;
}
return count;
异或运算的巧妙之处
性质
异或又称不进位加法,两个数相异或,对应位相同则为0,不同则为1;
具有以下性质:
1、a ^ a = 0;
2、0 ^ a = a;
3、异或具有交换律和结合律
b ^ c = c ^ b;
a ^ b ^ c = a ^ ( b ^ c) = ( a ^ b ) ^ c;
4、( -1 ) ^ a =!a;
应用
交换两个变量的值
交换变量a,b的值
a = a ^ b;
b = b ^ a;
a = a ^ b;
利用异或运算的交换律和结合律,可以得到如下:
1、a = a ^ b;
2、b = b ^ a;
把1式代入2式中,此时 b = b ^ ( a ^ b ) ,则 b = b ^ b ^ a = a;
3、a = a ^ b;
将1式和 b = a 代入3式,则 a = a ^ b ^ a = b;
不用判断语句,求整数绝对值
return (value ^ (value >> 31))-(value >>31)
1、若value为正数,则value二进制表示中最高位一定为0,那么 value >> 31 =0;
value ^ (value >> 31) = value ^ 0 = value;
value - (value >> 31) = value - 0 = value;
即正数的绝对值仍是自身;
2、若value为负数,则value二进制表示中最高位一定为1,那么 value >> 31 = 111…1 ,一共32个1,即-1;
value ^ (value >> 31) = ! value;
而负数以补码形式存放,补码等于绝对值的原码取反+1;
那么这里 ! value - (value >> 31) => ! value +1 即得到的是value的绝对值;
如果理解有困难,可以看这个例子:
如何找唯一成对的数?
问题描述:1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,在不用辅助存储空间的前提下,设计一个算法,将它找出来;
思路:根据异或性质1: a ^ a = 0;可以用来去重;
令T = 1 ^ 2 ^ 3 ^… ^ 1000 ;
那么遍历数组的同时将当前数字与 T 异或,在数组中只出现一次的数字会与 T 中的该数字相抵消,从而去重;最终会剩下重复的那个元素,因为它在数组和T中一共出现3次;
举一个只有11个数的例子: 重复的元素在任意位置出现都是可以找出来的;
int T=0;
for(int i=1;i<=1000;i++)
{
T=T^i;
}
for(int i=0;i<=1000;i++)
{
T=T^A[i];
}
return T;
如有错误,感谢指正!