LeetCode | Single Number(数组中的单个数字)

Given an array of integers, every element appears twice except for one. Find that single one.

Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

题目解析:

可以扩展

问题1:一个数组中,只有一个数字出现了一次,其余数字出现了两次,找出只出现一次的数字。

问题2:有n+1个数,分布在1-n之间,只有一个数据出现了两次,其余都出现一次,找出这个数据。

问题3:
一个n+2的数组中,只有2个数字出现了一次,其余数字出现了两次,找出这两个数字。
这些问题都是判断有出现奇数次有出现偶数次。是可以常用的不同问题可能有很多相关解法,但是通过异或来求解,是最合适的方法。


问题1
将所有的数据都异或在一起,如res = a[0]^a[1]…..^a[n-1] 。异或有个性质,a^a = 0;0^b = b;所有res的值就为只出现一次的那个数值。

class Solution {
public:
    int singleNumber(int A[], int n) {
        if(n == 0)
            return 0;
        int single = 0;
        for(int i = 0;i < n;i++)
            single = single ^ A[i];
        return single;
    }
};





问题2
对于问题2的解决,一开始想不到异或的方法,
我们可以通过相加再减来求解:将1到n+1个数据相加,然后再减去1到n的和;就得到了要求解的数值。因为题目特殊,范围限制在1到n之间,并且每个值一定至少出现一次。但是这个方法有个缺陷,如果n很大的时候会造成溢出,导致求解失败。
另外也可以用哈希来求解:建立一个1-n的哈希表,判断是否重复。但是会浪费大量空间。
既然有奇偶性的问题,我们可以将数组扩展成2n+1,多处的n个数据是1,2,3…n。这样的话,2n+1个数据中,“不重复”的数据,出现了两次,重复的数据出现了一次。其实我们也不必非要扩容容量,先循环,将res = 1^2…^n  然后再和n+1个数据异或。这样空间复杂度就为O(1)了。


问题3
这个问题一看无法下手,但是也是可以通过异或来解决的。
1、我们可以先将所有的数据都异或在一起,得到值x = a^b,那么x肯定不为0。找到x二进制表示中的第k位为1,比如是a,我们知道要找的a和b在这个位上肯定一个是0,一个是1。不会一样,那么可以通过这个位,将原n+2个数字分成两块,一块数据的该位是1(a在这个里面),另一块数据的该位是0(b在这个里面)。这样就可以将要求解的a和b分开。在分别用解决问题1的方法,来求解。
不过可以简化一下,通过求得的x,和其中一块数据第k位为1的所有数字相异或,就得到了b。求第a的时候,将原始数据x与刚求得的b异或就行了。

void getNum(int a[],int length)
{
	int s=0;//保存异或结果
	for(int i=0;i<length;i++)
	{
		s=s^a[i];
	}
	int temp1=s;//临时保存异或结果
	int temp2=s;//临时保存异或结果
	int k=0;
	while(!(temp1&1))//求位为1的位数
	{
		temp1=temp1>>1;
		k++;
	}
	for(int i=0;i<length;i++)
	{
		if((a[i]>>k)&1)//将s与数组中第k位为1的数异或
		{
			cout<<a[i]<<" ";
			s=s^a[i];
		}
	}
	cout<<s<<" "<<(s^temp2)<<endl;//(s^temp2)用来求另外一个数
}

点赞