最近逛技术论坛时看到一个网友出了一道题,大致是这样:有一个集合,里面包含N个整数,除了一个整数只出现一次之外,其他都出现3次,怎么样最快找出只出现一次的那个数?
作者的解法有点忘记了,但是这个题突然让我想起之前《编程之美》里的一道题, 和这个题的区别是其他都出现2次,只有一个是出现一次。 它的解法非常巧妙,就是把所有整数进行异或运算,最后的结果就是只出现一次的那个整数。因为异或会把相同的消除掉。但是这个题是出现3次,异或已经解决不了,第一想法是空间换时间,所有数中取最大值Max,然后定义一个数组int hashArr[Max],运用hash的思想,遍历所有整数进行hashArr[i]++运算,最后再找出hashArr[i]为1的,下标值就是所求的数。这个解法同样适用于其他出现N次的情况!解法时间复杂度为O(N),空间额外开销为S(MAX)
但是我们可以继续深入分析,以32bit整数为例,如果把所有数的第i个比特位相加,假如和为sum。则会有如下等式:3(x1 + x2 + ... + xn) + x0 = sum (x1代表第一个数的第i个比特位的值 X0代表只出现一次整数的第i个比特位的值) 从这个等式中可以看出x0的值为sum/3的余数,因为x0不是1就是0,肯定小于3。所以我们可以有如下的代码:
int arr[N] = {}; int _tmain(int argc, _TCHAR* argv[]) { int nMask = 1; int nBitSum = 0; int nDst = 0; for (int i = 0; i < 32; ++i) //依次判断32位 { for (int j = 0; j < N; ++j) //对N个数的第i位求和 { if (arr[j] & nMask) { nBitSum++; } } if (nBitSum%3 != 0) //余数不是1就是0 { nDst |= nMask; } nMask <<= 1;
nBitSum = 0; } return nDst; }
这样就把只出现一次的数求出来了,而且如果其他数出现的次数是M次的话,mod M就可以了,时间复杂度是O(32*N), 空间额外开销就是几个成员变量。
相比较而言还是下面的算法更好一些,当然也是具体情况而定。
时间: 2024-10-06 10:31:40