二进制位方法
集合的每个元素,都有可以选或不选,用二进制的位来表示,0表示不选,1表示选自。0x1 << nums.size()-1 的每一位就代表了集合中每个元素都选用。这里由于集合中每个元素都不相同,所以可以用二进制位唯一地表示它们。每次循环遍历该数的每位bit的状态可以判别是否加入其所表示的元素。
哈希表法:元素没有在哈希表中出现时,则加入哈希表,否则,将该元素从哈希表中删除,但是由于题目空间要求限制,故哈希表法不适用。
异或法:
我们只需要将所有的数进行 XOR 操作,得到那个唯一的数字。
这里最重要讲的是三进制法,是沿袭上一题的思路进行的。需要实现无进位的三进制计数器:使用ba来记录bit为1的数量(00->01->10->00)。从下图可以看到低位 a = (~b)&(~a)&(num) + (~b)&(a)&(~num)
也就是a = (a^num) & ~b
。但是注意的是因为这行更新了a的状态,高位b的表达式需要依赖于更新了的a,所以当b=1时,a都是为0的。故b的表达式为b = (b^num) & ~a
。
b | a | num | 更新后b | 更新后a |
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 0 | 1 |
0 | 1 | 0 | 0 | 1 |
0 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 0 |
更通用的解法:这个题其实就是求,在其他数都出现k次的数组中有一个数只出现一次,求出这个数。使用一个32维的数组,用这个32维的数组存储所有数里面第0位1的总数,第1位1的总数。。。第31位1的总数。假如第0位1的个数是k的倍数,那么要求的这个数在该位一定是0,若不是k的倍数,那么要求的这个数在该位一定是1,第1位的1一直到第31位的1的个数同理。为什么呢?因为假如说数组中的某些数在该位置是1,那么因为这个数要么出现k次,那么出现1次。
哈希法
巧用数字的不同
第一步: 把所有的元素进行异或操作,最终得到一个异或值。因为是不同的两个数字,所以这个值必定不为0;
第二步: 取异或值最后一个二进制位为1的数字作为mask,如果是1则表示两个数字在这一位上不同。
第三步: 通过与这个mask进行与操作,如果为0的分为一个数组,为1的分为另一个数组。这两个出现一次的不同的数被分在两个不同的数组。这样就把问题降低成了:“有一个数组每个数字都出现两次,有一个数字只出现了一次,求出该数字”。对这两个子问题分别进行全异或就可以得到两个解。也就是最终的数组了。
Boyer-Moore 投票算法:如果我们把众数记为 +1 ,把其他数记为−1 ,将它们全部加起来,显然和大于 0 ,从结果本身我们可以看出众数比其他数多。本质上, Boyer-Moore 算法就是找 nums 的一个后缀 suf ,其中 suf[0] 就是后缀中的众数。我们维护一个计数器,如果遇到一个我们目前的候选众数,就将计数器加一,否则减一。只要计数器等于 0 ,我们就将 nums 中之前访问的数字全部 忘记 ,并把下一个数字当做候选的众数。
[7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 7, 7, 7, 7]
首先,下标为 0 的 7 被当做众数的第一个候选。在下标为 5 处,计数器会变回0 。所以下标为 6 的 5 是下一个众数的候选者。由于这个例子中 7 是真正的众数,所以通过忽略掉前面的数字,我们忽略掉了同样多数目的众数和非众数。因此, 7 仍然是剩下数字中的众数。
[7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 5, 5, 5, 5]
现在,众数是 5 (在计数器归零的时候我们把候选从 7 变成了 5)。此时,我们的候选者并不是真正的众数,但是我们在 遗忘 前面的数字的时候,要去掉相同数目的众数和非众数(如果遗忘更多的非众数,会导致计数器变成负数)。因此,上面的过程说明了我们可以放心地遗忘前面的数字,并继续求解剩下数字中的众数。最后,总有一个后缀满足计数器是大于 0 的,此时这个后缀的众数就是整个数组的众数。
官方题解给的小trick:
int res = 0;
while(n != 0){
//将n 和 n−1 做与运算,会把最后一个 1 的位变成 0
n = n & (n-1);
res++;
}
return res;
不断把数字最后一个 1 反转,并把答案加一。当数字变成 0 的时候,我们就知道它没有 1 的位了,此时返回答案。这里关键的想法是对于任意数字 n ,将 n 和 n - 1 做与运算,会把最后一个 1 的位变成 0 。
在二进制表示中,数字 n 中最低位的 1 总是对应n−1 中的 0 。因此,将 n 和 n−1 与运算总是能把 n 中最低位的 1 变成 0 ,并保持其他位不变。
数字范围按位与利用了上面题目的技巧:如果m是n-1,从数字的二进制表示上可以发现n&m的结果是n的最后一个为1的bit被抹去的结果。也就是如果m处于n&(n-1) <= m <n中,[m,n]之间数的与运算结果即为n的最后一个为1的bit被抹去的结果。算法只要每次判断如果n>m,则说明n的最后一个为1的bit将不会出现。