数组中,只有一个/两个/三个数只有一个,其他都为偶数个,找出只有一个的数

16 篇文章 0 订阅
10 篇文章 0 订阅

解决方案1:

先排序,后比较,时间复杂度为O(nlogn+n)

解决方案2:

采用位运算。

因为 0^ a=a, a^a=0;

所以只有1个不同时,循环异或一遍,结果便是。


只有两个不同时,循环异或一遍,结果便是x=a^b,因为a!=b,所以x中必存在某一位为1,假设为第k位。那么a,b的第k位一个比为0,另一个必为1。

这样我们就可以根据第k位为0,还是1,将数组中所以元素划分为两个阵营,一个是第k为0,为a,c,c,d,d,.另一个是第k位为1,为b,e,e,f,f,..。这样我们就转化为第一个的问题了。

在每个阵营中循环异或一遍便得到相应的结果。


只有三个不同时,循环异或一遍,结果便是x=a^b^c,我们想可不可以转化为前面的问题。但是我们发现不能直接采用第二个问题的思路。

因为:1。如果x中存在某一位为1,假设为第k位。那么a,b,c的第k位为1,00或者111这两种形式。对于第一种可以采用划分的思路,将三个阵营缩小为两个阵营,即转化为第二个问题和第一个问题。对于第二种形式无法划分成两个阵营。

   2。x中必存在某位为1吗?答案为可能x的结果就是0

所以该思路不行。

我们可以这样,设数组为a,b,c,d,d,e,e,f,f,..先循环异或一遍,结果便是x=a^b^c,然后用x去异或数组中每个元素。即x^a,x^b,x^c,x^d,x^d...

即把原先数组为现在的数组A,B,C,D,D,E,E,F,F其中A=b^c, B=a^c, C=a^b,且A!=0,B!=0,C!=0(因为a,b,c 不相同)

然后对现在数组再异或一遍,结果为y=A^B^C=0

这时候我们可以把A^B看做一个整体,因为异或结果为0,则数必相同,即A^B=C。

由于C!=0,则C必存在某一位为1,假设为第k位。即A^B的第k位为1,则A,B的第k为要么为0要么为1,这样我们就可以把A,B,C,D,D,E,E,F,F数组中元素根据第k位为0还是为1划分为两个阵营,一个阵营中第k为1,有两个不同,一个阵营第k为0,有一个不同,直接分别使用我们前面的一个不同,两个不同的方法,我们就可以获得A,B,C,

由A,B,C获得a,b,c 这个就比较简单了。a=A^x,b=B^x,c=C^x

思路没问题了,但是在编程中,如果找到C,这是个问题。下面是小小的技巧使用。

    我们定义一个函数f(n),它的结果是保留数字n的二进制表示中的最后一位1,而把其他所有位都变成0。比如十进制6表示成二进制是0110,因此f(6)的结果为2(二进制为0010)。f(x^a)f(x^b)f(x^c)的结果均不等于0。

但是我们知道A,B,C中的f(A), f(B), f(C)的值必有两个相同,z=f(A)^f(B)^f(C) 我们就可以把数组A,B,C,D,D,E,E的函数值即f(A), f(B), f(C),f(D), f(D), f(E), f(E),根据他们的值和z相不相同进行划分。

#include<stdio.h>
int findOneNumber(int arr[],int n, int *num)
{
	int i;
	*num=0;
	for( i=0; i<n; i++)
	{
		*num=arr[i] ^ *num;
	}
	return 0;
}

int findFirstBitIs1(int num)
{
	int count=0;
	while( (num & 1)==0 && count <32)
	{
		num=num >> 1;
		count++;
	}
	return count;
}
int bitkIs1(int num, int k)
{
	while(k)
	{
		num=num >> 1;
		k--;
	}
	if( (num & 1)==1)
		return 1;
	else
		return 0;
}

int findTwoNumber(int arr[], int n, int *num1, int *num2)
{
	int i;
	int temp=0;
	for(i=0; i<n ;i++)
		temp=arr[i] ^ temp;
	int bitk=findFirstBitIs1(temp);
	int array1[n];
	int array2[n];
	int k1=0;
	int k2=0;
	for(i=0; i< n; i++)
	{
		if(bitkIs1(arr[i], bitk))
		{
			array1[k1++]=arr[i];
		}
		else
		{
			array2[k2++]=arr[i];
		}
	}
	findOneNumber(array1, k1, num1);
	findOneNumber(array2, k2, num2);
	return 0;
}

int findThreeNumber(int arr[], int n, int *num1, int *num2, int *num3)
{
	int temp=0;
	int i;
	for(i=0; i<n; i++)
		temp=temp ^ arr[i];
	//相当于构造了一个新的数组.原数组相当于a,b,c,d,d,e,e,现在为A,B,C,D,D,E,E
	for(i=0; i<n ;i++)
		arr[i]=arr[i] ^ temp;
	int flip=0;
	for(i=0; i<n; i++)
		flip=flip ^ findFirstBitIs1(arr[i]);

	int array1[n];
	int array2[n];
	int k1=0;
	int k2=0;
	//划分为两个部分,其实也可以优化空间使用情况,但是代码可读性差。
	for(i=0; i< n; i++)
	{
		if( findFirstBitIs1(arr[i])==flip )
		{
			array1[k1++]=arr[i];
		}
		else
		{
			array2[k2++]=arr[i];
		}
	}
	if(k1 % 2==0)
	{
		findTwoNumber(array1, k1, num1, num2);
		findOneNumber(array2, k2, num3);
	}
	else
	{
		findOneNumber(array1, k1, num1);
		findTwoNumber(array2, k2, num2, num3);
	}
	*num1 ^=temp;
	*num2 ^=temp;
	*num3 ^=temp;
	return 0;

}
int main()
{
	int a[]={2, 1, 2, 1, 5, 1, 5, 1, 4};
	int len=sizeof(a)/sizeof(int);
	int num1,num2,num3;
	findOneNumber(a, len, &num1);
	printf("number is %d \n",num1);

	int b[]={2, 4, 2, 2, 2, 2, 4, 4};
	len=sizeof(b)/sizeof(int);
	findTwoNumber(b, len, &num1, &num2);
	printf("num1: %d num2: %d\n",num1, num2);

	int c[]={1, 4, 2, 4, 4, 5, 5};
	len=sizeof(c)/sizeof(int);
	findThreeNumber(c, len, &num1, &num2, &num3);
	printf("num1: %d num2: %d num3: %d\n", num1, num2, num3);
	//printf("%dth bit is 1 \n" ,findFirstBitIs1(4));
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值