上篇谈到了用异或来解决,确实是个好方法,时间复杂度为o(n),比例一遍ok,空间复杂度为o(1),只占用一个空间足矣。现在把这个问题升级下:
(1)给出n个数,其中有且仅有一个出现了奇数次,其余的都出现了偶数次。用线性时间常数空间找出这个出现奇数次的数
(2)给定n个数,其中有且仅有两个出现了奇数次,其余的都出现了偶数次。用线性时间常数空间找出这两个出现奇数次的数
原理(原理不是很懂的,先看看上篇)
- 任何数和自己异或为0
- 任何数和0异或为自己
- 异或具有交换律
思路
(1)一个出现奇数次
- 出现偶数次的一异或为0了,对剩下的奇数次数不造成干扰
- 奇数次(2n+1)的前2n次一异或为0了,对剩下那个数不造成干扰
- 剩下的那个数就是结果
(2)两个出现奇数次
- 常规的从头到尾异或一遍,得到数肯定不为0,这个数是那两个出现奇数次的数异或的结果
- 找出这个数中不为1的那个位pos(在这个位置处,两个奇数次的数肯定不同——要是相同这个位也是0)
- 整个序列根据位pos的值分成两组(0的一组,1的一组,这样把出现偶数次的分到一组,无碍。出现奇数次的分到两组,正好)
- 对着两组,利用(1)的方法,解决
细节:如何找到一个二进制中第一个是"1"的位
1 #include <stdio.h> 2 3 #define N 10 4 5 int main() 6 { 7 int a[N] = {2, -11, -11, 2, 43, 5, 564, 43, 5, -3}; 8 int i, t; 9 int pos = 0, left = 0, right = 0; // remember to initilize! 10 11 t = a[0]; 12 for (i = 1; i < N; i++) 13 t ^= a[i]; 14 15 while (t % 2 == 0) { 16 t >>= 1; 17 pos++; 18 } 19 20 for (i = 0; i < N; i++) { 21 if (a[i] & (1 << pos)) 22 left ^= a[i]; 23 else 24 right ^= a[i]; 25 } 26 27 printf("they are: %d & %d ", left, right); 28 29 return 0; 30 }