一个数组中除了三个数字之外,其余数字都出现了两次,找出这三个数字

来源:互联网 发布:windows怎么解压war 编辑:程序博客网 时间:2024/05/21 09:16

分析:

假设只出现一次的三个数字是a,b,c

1. 首先遍历数组,xor代表所有值的异或和,最终,xor = a^b^c;

2. 证明xor与a,b,c值得都不一样。如果xor与a值一样,那么xor = a^b^c = a, 也就是b^c=0,也就是b等于c,这与题目矛盾,所以xor与a,b,c的值都不一样,也就是xor^a,xor^b,xor^c都不为0;

3. 函数find_1bit()(以下简称f())用于查询某个数中从右边扫描比特1第一次出现的位置,比如6,二进制为0110,通过f()返回0010;

4. 证明f(xor^a)^f(xor^b)^f(xor^c)的值不为0。因为f(xor^a)^f(xor^b)的值要不为0,也不出现两个比特1,而f(xor^c)只有一个比特1,二者肯定不相等,所以f(xor^a)^f(xor^b)^f(xor^c)的值不为0;

5. f(xor^a)^f(xor^b)^f(xor^c)的值至少有一位为1。假设从右边扫描第一个1出现的位置在m位,那么f(xor^a)、f(xor^b)、f(xor^c)在m位的数值要不全为1,要不只有一个为1,另两个为0;

6. 证明f(xor^a)、f(xor^b)、f(xor^c)在m位的数值不全为1。假设全为1,那么在m位上,a,b,c的值相同,与xor相反。如果a、b、c值在m位上全为0,那么xor=a^b^c=0,与假设矛盾。如果a、b、c值在m位上全为1,那么xor=a^b^c=1,也与假设矛盾。所以f(xor^a)、f(xor^b)、f(xor^c)在m位的数值不全为1,只有一个为1,另两个全为0.根据这一位的1,我们可以分为两组,分别进行求解;

#include <stdio.h>int find_1bit(int n){return n&~(n-1);}void get_two(int a[], int n, int *num1, int *num2){int xor;int i;for(i=0;i<n;i++)xor ^= a[i];xor = find_1bit(xor);for(i=0;i<n;i++)if(a[i]&xor)*num1 ^= a[i];else*num2 ^= a[i];}void get_three(int a[], int n, int *num1, int *num2, int *num3){int xor = 0;int f = 0;int i;int temp;for(i=0;i<n;i++)xor ^= a[i];for(i=0;i<n;i++)f ^= find_1bit(xor ^ a[i]);f = find_1bit(f);for(i=0;i<n;i++)if(find_1bit(xor^a[i]) == f)*num1 ^= a[i];for(i=0;i<n;i++)if(a[i] == *num1){temp = a[i];a[i] = a[n-1];a[n-1] = temp;}get_two(a,n-1,num2,num3);}int main(){int a[] = {2,2,3,3,4,4,5,6,7};int num1 =0,num2 = 0,num3 = 0;int i;printf("原数组为:\n");for(i=0;i<9;i++)printf("%d\t",a[i]);printf("\n");get_three(a,9,&num1,&num2,&num3);printf("只出现1次的三个数字:\n");printf("%d\t%d\t%d\t",num1,num2,num3);printf("\n");return 0;}




原创粉丝点击