当然了,O(1)空间复杂度是必须的...
先看一个简单版:
求出一个序列中一个只出现一次的数
COJ 1217 奇数个的那个数
http://122.207.68.93/OnlineJudge/problem.php?id=1217
我们知道任意两个相同的数 异或结果为0 任何数与0异或结果是其本身 异或运算满足交换律
亦即:a^a=0 a^0=a (a^b)^(a^b)=(a^a)^(b^b)=0^0=0
这样我们就得到了一个用异或运算的解法
1 #include<stdio.h> 2 int main() 3 { 4 int n,a,res; 5 while(scanf("%d",&n)!=EOF) 6 { 7 res=0; 8 while(n--) 9 { 10 scanf("%d",&a); 11 res^=a; 12 } 13 printf("%d ",res); 14 } 15 return 0; 16 }
现在考虑加强版:找出一个序列中两个只出现了一次的数
COJ 1240 低调,低调
http://122.207.68.93/OnlineJudge/problem.php?id=1240
我们可以利用上面的思路,假设这两个数是a和b 则按照上面的方法得到的res=a^b
因为a和b是两个不同的数,所以它们的二进制表示中,至少有一位是不同的,也就是说res的二进制表示中至少有一位是1
我们可以根据这一位将这两个数分开来.
具体做法就是:
1、先遍历一遍数组,得到res=a^b
2、然后通过k<<i&res 的方式找到res的那一位1
3、最后根据这一位是0和1 把整个数组分为两部分 设两个ans ans1和那一位是0的去异或 ans2反之 则最后ans1 和 ans2刚好就是a和b
具体见代码:
1 #include<stdio.h> 2 int main() 3 { 4 int n,k,a,result,judge,ans1,ans2; 5 while(~scanf("%d%d", &n, &k)) 6 { 7 result=0; 8 ans1=0; 9 ans2=0; 10 for(int i=0;i<k;i++) 11 { 12 scanf("%d", &a); 13 result^=a; 14 } 15 for(judge=1;judge<=result;judge<<=1) 16 { 17 if(judge&result) 18 { 19 break; 20 } 21 } 22 for(int i=0;i<k;i++) 23 { 24 scanf("%d", &a); 25 if(judge&a) 26 { 27 ans1^=a; 28 }else 29 { 30 ans2^=a; 31 } 32 } 33 if(ans1>ans2) 34 { 35 printf("%d %d ", ans2, ans1); 36 }else 37 { 38 printf("%d %d ", ans1, ans2); 39 } 40 } 41 return 0; 42 }