异或
表示当两个数的二进制表示,进行异或运算时,当前位的二进制相同为0,不同为1.
表示为:
- 0 ^ 0 = 0
- 1 ^ 0 = 1
- 0 ^ 1 = 1
- 1 ^ 1 = 0
特点:
- 0异或任何数,是任何数;
- 1异或任何数,任何数取反;
- 任何一个数字异或自己都等于0
面试题:一个整型数组中除了两个数字之外,其他的数字都出现了两次。试找出这两个只出现一次的数字。《剑指offer》
例如,数组{1,2,3,4,5,6,4,3,2,1} 中,5和6只出现了一次,其他都出现了两次。
这个问题想了很长时间,一直无解。
题中提到有两个出现一次的数字,先尝试解决数组中只有有一个出现一次的数字的。
比如,
1 ^ 2 = 3
1 ^ 3 = 2
2 ^ 3 = 1
1 ^ 1 = 0
2 ^ 2 = 0
3 ^ 3 = 0
这里利用异或的第三个特点,任何一个数字异或自己都等于0.我们从头到尾依此异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些成对出现的数字全部都在异或中抵消了。
如,{1,2,3,1,2},1 ^ 2 ^ 3 ^ 1 ^ 2 = 3.
现在找只出现一次的两个数字,如{1,2,3,4,3,1,6,2,5},
1 ^ 2 ^ 3 ^ 4 ^ 3 ^1 ^ 6 ^ 2 ^ 5 = 5 ^ 6.
从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的疑惑结果。因为其他数字都出现了两次,在异或中全部抵消了。
由于两个数字不一样,异或结果不为0,也就是说在这个结果数字的二进制中至少有一位为1.
如上图,以第二位1为标准(第一位1也满足),把原数组中的数字分为两个子数组,第一个子数组中每个数字的第二位都是1,而第二个子数组中每个数字的第二位都是0.
由于我们分组的标准是数字中的某一位是1还是0,那么出现了两次的数字一定会被分配到同一个子数组。这样每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。
//代码实现
unsigned int Find_First_bitIs_1(int num)
{
int index = 0;
while(((num & 1) == 0) && (index < 8*sizeof(int)))
{
num = num >> 1;
++index;
}
return index;
}
bool IsBit_1(int num, unsigned int index)
{
num = num >> index;
return (num & 1);
}
void FindNum_Once(int array[], int length, int* num1, int* num2)
{
if(array == NULL || length < 2)
return;
int res_OR = 0;
for(int i=0; i < length; ++i)
res_OR ^= array[i];
unsigned int index = Find_First_bitIs_1(res_OR);
*num1 = *num2 = 0;
for(int j=0; j < length; ++j)
{
if(IsBit_1(array[j], index))
{
*num1 ^= array[j];
}
else
{
*num2 ^= array[j];
}
}
}
实现交换两个数
void Swap(int* a, int* b)
{
*a ^= *b ^= *a ^= *b;
}
int main()
{
int a = 10;
int b = 20;
Swap(&a, &b);
printf("%d,%d\n",a,b);
return 0;
}
使特定位反转
例如,有01011011,想要高四位取反,可以将它和11110000进行 ^ 运算,
01011011 ^ 11110000 = 10101011
与0相^保留原值。
参考资料:
《程序员的自我修养》
《剑指offer》