您的位置:首页 > 其它

三个数是唯一出现的,其余的都出现偶数个,找出这三个数中。

2016-01-16 21:06 323 查看
题目:一个数组里,除了三个数是唯一出现的,其余的都出现偶数个,找出这三个数中。

比如数组元素为[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\n", groupA);
			return groupA;
		}
		if (cntB % 2 == 1 && groupA != 0)
		{
			//B组是奇数个元素,另外两个不同的数在A组(A组的异或值就不等于零)。
			printf("%d\n", 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\n", groupA, groupB, difNum);<span style="white-space:pre">		</span>//分出了三个数
	system("pause");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: