Leetcode: SingleNumber I & II 数组中只出现一次的数

     题目:I:Given a non-empty array of integers, every element appears two times except for one, which appears exactly once. Find that single one.
       Note:  Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
       在一个数组中,除了一个数字出现一次,其他都出现两次。请找出这个数
      注意:你的算法应该是一个线性复杂度。

     这个题目就是让你在O(n)的时间复杂度下,找出那个只出现一次的数。对于没有接触过这类题的人,乍一看无从下手。其实如果你知道了位操作的话这题也就不难解了: 在位运算中,异或操作符能够很好地将两个数“抵消”    因为相同的两个数,它们每位的值都是相等的。我们知道相同的位(0,1)异或的结果为0 ,这样两个相同的数(这里可以扩展到偶数个)异或结果为0,而任意数与0异或结果都为本身。有了这个思路,这个算法也就很简单了。

//* 出现两次 找到只出现一个的那个数

int SingleNumber_d(int[] a,int len)
{
	int res = 0;
	for(int i=0;i<len;++i)
	{
		res=res^a[i];
	}
	return res;
}

是不是很简单呢,其实 推广到所有偶数个数,都可以实现,这就是异或的巧妙运算。

进阶版:II:Given a non-empty array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

给出一个数组,除了一个数出现一次,其他都出现3次,请找出这个数。时间复杂度线性范围内。

前面的偶数个数可以用异或来实现。那么奇数个数的又如何来实现呢?

     一种是考虑二进制的方式,利用“位相加”的方法,对于int型的数,我们可以定义一个bits[32]的数组,在二进制层面上,按位进行相加,如果数组的数都出现3次的话,那么对于每一位相加的和%3必然等于0,现在多了个只出现一次的数,对3取余结果不为0 的那位,必然是只出现一次的数在此位上为1。这样我们就能计算出这个数了。

大家可以对照着code理解:

//*  记录32位的各位位数和,%3,

int SingleNumber_t1(int[] a,int len)
{
	int bits[32]={0};  // 用于记录每位的和
	
	for(int i=0;i<len;++i)
	{
		for(int j=0;j<32;++j)
		{
			bit[j] += (a[i]>>j)&1   // 位操作,记录求和
		}
	}
	// 对求和结果取余,不为0的则为只出现一次的。最后转为10进制结果。
	int res=0;
	for(int j=0;j<32;j++)
	{
		if(bits[j]%3 != 0)
			res += 1<<j;
	}
	return res;
}

核心思路就是以位的层面上进行操作,推广一下的话,其实出现任意N次,都可以用这种方法来求解。

第二种思路就比较厉害了,是Leetcode上的一位大神写的,原理上还是利用“抵消”的思想,出现两次一个异或就能解决,出现三次的就需要更加复杂的操作了(0,1,2)。整体解决思路就是 跑三遍然后能“抵消”。

// *进阶版
// *出现三次  找到只出现一个的那个数
int SingleNumber_t2(int[] a,int len)
{
	int a=0,b=0;
	for(int i=0;i<len;++i)
	{
		a = (a^a[i]) & ~b;
		b = (b^a[i]) & ~a;      // * what the fuck?
	}
	return a;
}

是不是看的很懵逼,只能说能想到这种写法的人,对于位操作的理解是相当的深入了。

猜你喜欢

转载自blog.csdn.net/qq_38506897/article/details/81366497