题目:一个数组里,除了三个数是唯一出现的,其余的都出现偶数个,找出这三个数中。
比如数组元素为[1,1,2,2,3,3,4,5,6],只有4,5,6这三个数字是唯一出现的,我们只需要输出4,5,6中的一个就行。
思路:
1.这个数组元素个数一定为奇数
假如有103个数,则有50对数,加上三个不同的数,则这三个和50对中的任何一个都不同。
2.因为3个数不相同, 三个数一定不可能每一bit位都相同。 如果每个bit位都相同,那么十进制的值也一定相等的。
我们可以找到其中一个数的bit位与其他两个不同,可以把那一个数从三个数字中分出来.
3.三个数肯定可以分到两组不同的数组里面去,基于这样的思路就可以找出这三个不同数字中的一个.
因为一位bit位只能区分两种状态,所以根据其中一个数的bit位与其他两个不同,可以把三个数中的二个分到一组,一个分到另一组。
下面用到异或的性质
1.A^A = 0 两个相同的数异或一定等于0
2. A^0 = A 任何数和0异或都等于0本身
比如数组元素为[1,1,2,2,3,3,4,5,6] 2的二进制是 010 ,3的二进制是011 ,如果按最后一位分,
最后一位是0在第一组,最后一位是1在第二组,那么两个2 一定都在第一组,两个3 一定都在另二组,然后
2^2 与3^3 的异或结果就都等于0. 0在与其他的单个数异或,结果就等于那个数了。
将找出的数和所有数异或,相当于又放入数组一个这个数, 这时候数组中就只有两个不同的数了。
考虑“只有两个数出现一次”的情况:可以找到一种方法,将数组划分为两部分,且让这两个数分别在不同部分,
这样每部分所有数的异或值,恰好分别等于这两个数。一种简单的分法就是,先计算出这两个数的异或值M(等价于求数组中所有数的异或值),
求出M值的二进制表示中的最低位1(其它位的1也可以,只不过麻烦点)在 第k位,然后根据第k位是否为1,将原数组分为两部分。
如4,5,6的二进制分别为0100,0101,0110。因为5的的倒数第一位为1,而其他的为0,程序第一个输出的就是5了。
程序如下:
#include<stdio.h> #include<stdlib.h> #define bit(t,i) ( (t) & (1 << (i) )) #define N 9 int findOneDif(int arr[], int len) { int groupA, groupB, cntA, cntB, i, j; for (i = 0; i <= 31; i++) { groupA = groupB = cntA = cntB = 0; //全部初始化为0 for (j = 0; j < len; j++) { if ( bit(arr[j], i)) //按倒数第i位分组,第i位为1在第一组,第i位为0在倒数第二组 { groupA ^= arr[j]; cntA++; } else { //groupB ^= arr[j]; cntB++; } } if (cntA % 2 == 1 && groupB != 0) { //A组是奇数个元素,另外两个不同的数在B组(B组的异或值就不等于零)。 printf("%d ", groupA); return groupA; } if (cntB % 2 == 1 && groupA != 0) { //B组是奇数个元素,另外两个不同的数在A组(A组的异或值就不等于零)。 printf("%d ", groupB); return groupB; } } } int findLastBit1(int n) { int i; for (i = 0; i < 32; i++) { if (n & (1 << i)) { return i; } } } int main() { int arr[] = { 1,1,2,2,3,3,4,5,6 }; int bit_pos,difNum, i,a_b, groupA, groupB, dist; difNum = findOneDif(arr, N); //找出一个出现一次的数,一会将找出的这个数和所有的数异或 a_b = 0; for (i = 0; i < N; i++) //(相当于找出的这个数在数组中出现2次)结果就等于另外两个不同的数异或, { //因为除了三个不同的数,其他的数异或结果本来就为零。 a_b ^= arr[i]; } a_b ^= difNum; //求出两个数的异或值 bit_pos = findLastBit1(a_b); //找出异或结果最后一位不为1的位置,假设为k位 dist = (1 << bit_pos); groupA = groupB = 0; for (i = 0; i < N; i++) { if (arr[i] == difNum) //将数组中找的那一个不同的数排除掉 continue; if (arr[i] & dist) //k位为1,分第一组 { groupA ^= arr[i]; } else { //k位为0,分第二组 groupB ^= arr[i]; } } printf("%d %d %d ", groupA, groupB, difNum);<span style="white-space:pre"> </span>//分出了三个数 system("pause"); }