题目: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;
}
是不是看的很懵逼,只能说能想到这种写法的人,对于位操作的理解是相当的深入了。