您的位置:首页 > 其它

[Leetcode] Single Number I | Single Number II

2014-07-19 15:42 323 查看
题目一: Given an array of integers, every element appears twice except
for one. Find that single one.

要求:O(n)时间复杂度,不使用额外空间。

一个数组中其他元素都出现两次,只有一个出现一次,要找到那个出现一次的。因为是需要线性复杂度,就不能用两重循环,又因为不能用额外空间,所以不能用hash,参考了网上的境地算法,都是用位操作实现的。所以这是一个考察位操作功底的题目。

先介绍异或操作,即两个数相同取0,不同则取1;性质:任何一位二进制数同
1 异或都是取反,任何一位二进制数同 0 异或都保持不变。这条性质最直观的应用就是不用额外变量交换两个数。

void exchange(int &a, int &b)

{
b=a^b;                  //b'每位的0和1 表示a,b对应位相同和不同
a=a^b;                  //a^0得到相同的,a^1得到b的那位
b=b^a;                  //同样b'^a(b)会得到a
}

还有个性质是a ^b ^c
= a ^ (b ^ c)
= (a ^ b) ^ c,就是说1^2^3^4^4^3^2=2^2^3^3^4^4^1=1,这就知道怎么解决上面的问题了。

int singleNumber(int A[], int n)
{
// Note: The Solution object is instantiated only once and is reused by each test case.
if(A == NULL ){
return 0;
}
int result = A[0];

for(int i = 1; i < n; i++){
result = result ^ A[i];
}
return result;
}


题目二:Given an array of integers, every element appears three times
except for one. Find that single one.

要求:O(n)时间复杂度,不使用额外空间。

这次是其他数都出现了三次,只有一个出现一次,找到这个数。上面的方法就不能用了,但思路还是位操作,利用三个变量ones,
twos , threes分别保存各个二进制位上
1 出现一次、两次、三次的分布情况,如代thress=0100,这表明第三位出现过三次1。我们用与或操作记录某位上出现的1,用与操作记录1再次出现的情况。 最后只需返回变量ones就行了。码如下:

int singleNumber(int A[], int n)
{
int ones = 0, twos = 0, threes = 0;
for(int i = 0; i < n; i++)
{
threes = twos & A[i];		//1已经出现两次并且再次出现
twos = twos | ones & A[i];	//1出现两次,ones & A[i]=1的表示 那位还是1
ones = ones | A[i];		//1出现一次, ones | A[i]=1 表示那位上出现了1

twos = twos & ~threes;           //当某一位出现三次后,我们就从出现两次中消除该位
ones = ones & ~threes;           //当某一位出现三次后,我们就从出现一次中消除该位
}
return ones;                             //twos, threes最终都为0.ones是只出现一次的数
}


       还在网上发现了一种通用解法,处理其他数据出现k次。 int 数据共有32位,可以用32变量存储 这 N 个元素中各个二进制位上 1 出现的次数,最后在进行模k操作,如果为1,那说明这一位是要找元素二进制表示中为 1 的那一位。代码如下:

int singleNumber(int A[], int n) {
int bitnum[32]={0};
int res=0;
for(int i=0; i<32; i++){
for(int j=0; j<n; j++){
bitnum[i]+=(A[j]>>i)&1;
}
res|=(bitnum[i]%3)<<i;
}
return res;
}
继续扩展:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
分析:如果能够把原数组分为两个子数组。在每个子数组中,包含一个只出现一次的数字,而其他数字都出现两次。如果能够这样拆分原数组,按照前面的办法就是分别求出这两个只出现一次的数字了。

我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0,也就是说在这个结果数字的二进制表示中至少就有一位为1。我们在结果数字中找到第一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0。现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。因此到此为止,所有的问题我们都已经解决。

unsigned int FindFirstBitIs1(int num)
{
int indexBit = 0;
while (((num & 1) == 0) && (indexBit < 32))
{
num = num >> 1;
++ indexBit;
}

return indexBit;
}
bool IsBit1(int num, unsigned int indexBit)
{
num = num >> indexBit;

return (num & 1);
}
void FindNumsAppearOnce(int data[], int length, int &num1, int &num2)
{
if (length < 2)
return;

// get num1 ^ num2
int resultExclusiveOR = 0;
for (int i = 0; i < length; ++ i)
resultExclusiveOR ^= data[i];

// get index of the first bit, which is 1 in resultExclusiveOR
unsigned int indexOf1 = FindFirstBitIs1(resultExclusiveOR);

num1 = num2 = 0;
for (int j = 0; j < length; ++ j)
{
// divide the numbers in data into two groups,
// the indexOf1 bit of numbers in the first group is 1,
// while in the second group is 0
if(IsBit1(data[j], indexOf1))
num1 ^= data[j];
else
num2 ^= data[j];
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode 位操作