闲暇之余逛到了lintcode,便打算做一些题目,做时却发现很多东西已然忘却,为了记住重新思考时的思路,就有了这个博客,水平不高,仅做笔记。
原题:
给出2*n + 2个的数字,除其中两个数字之外其他每个数字均出现两次,找到这两个数字。
看到题目的第一反应就是穷举解法(对于计算机而言,很少有不能用穷举来解决问题的,但实际上存在这个想法并不好,奈何本人算法确实较弱,拿到题目一般反应就是穷举解法,希望以后能有进步)。
穷举解法在这里的思路就是:判断一个数是否单次出现,只需遍历之后的数中有没有在出现即可。一个无关紧要的trick是,用一个数组记录已经匹配的数的index,这样可以不用每次都从头开始比较,也可以略过已经匹配的数。代码如下:
class Solution {
public:
/*
* @param A: An integer array
* @return: An integer array
*/
vector<int> singleNumberIII(vector<int> &A) {
// write your code here
vector<bool> vecFlag;
vecFlag.resize(A.size(), 0);
vector<int> ret;
for ( int i = 0; i < A.size(); ++i )
{
if ( vecFlag[i] )
{
continue;
}
int j = i + 1;
for ( ; j < A.size(); ++j )
{
if ( vecFlag[j] )
{
continue;
}
if ( A[i] == A[j] )
{
break;
}
}
if ( j >= A.size() )
{
ret.push_back(A[i]);
if ( 2 == ret.size() )
{
return ret;
}
}
else
{
vecFlag[j] = 1;
}
}
return ret;
}
};
上诉方法时间复杂度:T = (n-1))+ (n -2)+(n - 3)…. + 1; ⇒ O(N^2), 空间复杂度: O(N)
此段代码在lintCode上提交后,测试用例跑了一部分之后,会有超时的信息。那么此时不妨看一下挑战的要求:
O(n)时间复杂度,O(1)的额外空间复杂度
看到O(1)的额外空间时,我在想这个空间能做什么,于是我画了下面这样的一个草图。
对于临时空间而言,在A,B,A 遍历之后,临时空间中得到的数最好是什么,显然为B,那么可以抽象为 A[]B[]A[] = B ,[]中目标操作,什么操作可以去掉相同的数,这是抑或就从我脑海里显现出来了。通过对整个数组进行抑或操作之后,可以得到B^D的值。
剩下的问题是如何将 B和D从 B^D的值中分离出来了。此时我首先想到了将B^D的值与数组进行第二次遍历,第二次遍历过程中肯定会发生如下情况:
B^D^B^D的情况,如果某次抑或之后,值为0,那么此刻就可以得到D, 剩下来B就好确定了,但遗憾的是,实际过程中存在如下的异常情况:
B^D = C, C^C = 0 此时计算的B和D就都是错的了。
思路到这突然就卡住了,一时都怀疑抑或的方法是否可行,但由于暂时想不到其他方法,于是我整理了一下目标,和现有的策略。
目标是要找出两个不同的数,策略是通过抑或去掉相同得数。 整理之后发现如果只有一个不同的数,那么通过抑或不就可以很方便的得到吗。只要把B和D分开在两个数组里就可以解决了。
因 B != D , 所以 B^D != 0, 设 B^D = E, 若E的二进制中第K位不等于0(肯定存在一个k,因为E!= 0),那么B与D的第K bit位肯定不同,根据第K bit位是否为1,将数组array中的数分为array_b和array_d两组,分别对两组数组求抑或就可以计算出B和D了。
此方案的代码如下:
class Solution {
public:
/*
* @param A: An integer array
* @return: An integer array
*/
vector<int> singleNumberIII(vector<int> &A) {
// write your code here
// first round find x^y
int tmp = 0;
for ( int i = 0; i < A.size(); i++ )
{
tmp ^= A[i];
}
// find bit k
int k = 0;
while( tmp %2 == 0 )
{
k++;
tmp >>= 1;
}
// second round find x y
int x = 0, y = 0;
for ( int i = 0; i < A.size(); i++ )
{
if ( (A[i] >> k) & 1 )
{
x ^= A[i];
}
else
{
y ^= A[i];
}
}
vector<int>ret;
ret.push_back(x);
ret.push_back(y);
return ret;
}
};
时间复杂度和空间复杂度均能达到要求
总结:该方案的关键在于使用抑或来消除相同的数,对于经常写算法的人来说可能很容易想到,但对于一个不甚熟悉的人来说,如何想到使用抑或来做才是重要的。