szhan46

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

上篇谈到了用异或来解决,确实是个好方法,时间复杂度为o(n),比例一遍ok,空间复杂度为o(1),只占用一个空间足矣。现在把这个问题升级下:

(1)给出n个数,其中有且仅有一个出现了奇数次,其余的都出现了偶数次。用线性时间常数空间找出这个出现奇数次的数

(2)给定n个数,其中有且仅有两个出现了奇数次,其余的都出现了偶数次。用线性时间常数空间找出这两个出现奇数次的数

原理(原理不是很懂的,先看看上篇

  • 任何数和自己异或为0
  • 任何数和0异或为自己
  • 异或具有交换律

思路

(1)一个出现奇数次

  1. 出现偶数次的一异或为0了,对剩下的奇数次数不造成干扰
  2. 奇数次(2n+1)的前2n次一异或为0了,对剩下那个数不造成干扰
  3.  剩下的那个数就是结果

(2)两个出现奇数次

  1. 常规的从头到尾异或一遍,得到数肯定不为0,这个数是那两个出现奇数次的数异或的结果
  2. 找出这个数中不为1的那个位pos(在这个位置处,两个奇数次的数肯定不同——要是相同这个位也是0)
  3. 整个序列根据位pos的值分成两组(0的一组,1的一组,这样把出现偶数次的分到一组,无碍。出现奇数次的分到两组,正好)
  4. 对着两组,利用(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\n", left, right);
28 
29         return 0;
30 }

 

posted on 2013-07-05 20:16  szhan46  阅读(373)  评论(0编辑  收藏  举报