异或操作与数据结构。
1.异或操作介绍
一、异或操作符号
c = a^b.
二 、异或操作定义
a | b | c = a^b |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 0 |
三、异或性质的延伸
0 ^ N = N
零与任意数异或仍为其自身N ^ N = 0
任意值相同的数异或结果恒为零A ^ B ^ C = B ^ A ^ C
即异或满足交换律(A ^ B)^ C = A ^ (B ^ C)
即异或满足结合律
如何理解异或操作的交换律与结合律?或者说为什么异或操作满足结合律与交换律?
如果单单从数的按位异或层面,确实很难理解。但异或操作还有一个性质,便是异或操作等价于无进位的加法操作。读者可以自行演算,即每一位相加,产生的进位位不对高位传递。
那么,从无进位的加法层面,交换律与结合律变很好理解了,即由于加法操作的交互律与结合律,加之不产生进位。很容易推出异或操作的结合律与交换律。
异或数据交换算法
根据以上性质,对于任意具有独立存储空间的数,有如下算法可以在不需要第三个变量的情况下,完成 数组中两数的交换(注意,两数不能为同一存储单元,否则会直接将其清零)
void swap (int * arr , int i, int j){
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
让我们举个例子来更形象的理解上述过程:
- 令 a = 甲 ; b = 乙;
- a = 甲 ^ 乙 ; b = 乙;
- b = ( 甲 ^ 乙) ^乙 = 甲 ^ (乙 ^乙)=甲
- a = ( 甲 ^ 乙) ^甲=(甲 ^ 甲) ^乙 = 乙
根据以上1~3步,便完成了在不使用新内存的情况下,数据ab的交换。
数据结构处理案例
- 假设有 N 个数,其中 只有一种数出现了奇数次,其余种数均出现了偶数次。
/
问: 在算法时间复杂度O(n)与空间复杂度O(1) 的约束下,如何找出N个数中出现奇次个数的数?
答:
int findNum(int *arr, int n){
int eor = 0;
for(int i=0;i<n;i++){
eor ^= arr[i];
}
return eor;
}
2.同样假设有N个数,其中 有两种数出现了奇数次,其余种的数均出现了偶数次。
问: 在算法时间复杂度O(n)与空间复杂度O(1) 的约束下,如何找出N个数中出现奇次个数的数?
void findNum(int *arr , int n ){
int eor1 = 0, eor2 = 0;
for(int i=0;i<n;i++){
eor1 ^= arr[i];
}
int rightOne = eor1 & (~eor1 + 1) ; //提取出最右侧的“1”
for(i = 0;i<n; i++){
if( (arr[i]&rightOne) == 0 ){ //当该数中与提取出“1”的位为 0 时,则选取;
eor2 ^= arr[i];
}
}
eor1 = eor1^eor2;
printf("The Numbers are %d and %d",eor1,eor2);
return;
}
这里简单讲解一下第二问的思路。首先按照第一问的思路,将数组中所有数异或之后,应得到的eor1 = a ^ b(假设所求两数为a 、b)。那么,对于eor1中,必定至少有一位为“1”(因为a 与 b不同)。
那么。我们便可以通过任意提取出一位不为零的“位”(假设D3 = 1)。再对数组中所有数进行分类,D3位为“1”的分为一类,“0”为另一类。那么对于a b 来说,一定分别在这两类之中。那么这时我们对其中任意一类进行依次异或,便可求出其中一个数。
最后,再将该数与最开始的eor1异或,便可得到另外一个“奇数次”数。