1.数组中只有一个数字出现1次,其他数字出现两次,找到只出现一次的这个数字。(题目还可以改成只有一个数字出现2(1)次,其他数字出现3次,找出那个except one)
思路:这类题实际是位运算的题,所有数字出现两次,只有个出现一次, 异或操作,都异或一遍,最后剩下的就是要求的那个数字了,那如果所有数字都出现3次呢?
可以实际不论是出现两次还是三次,我们的目的都是把这些消除掉,求最后剩下的那个,那么可以看成是二进制求模运算,数组中的数字都是32位int类型,那么一个数字如果出现两次,对应二进制上为1的位就出现2次,这时这个位应该消除为0(每个数字只出现两次),所以就是二进制模2,那么每个数字都出现3次就是二进制模3.
对于每个数字出现三次的题目,难点在于我们怎么找到一种运算等价于对数字的每个位做二进制模3呢?用两个数字ones,twos记录每一位当前的状态,用X表示下一位到来的是0还是1,可以写一个真值表啦,通过真值表能求得模3的逻辑表达式。那么最后ones代表的数就是只出现一次的那个数,而twos代表的就是只出现两次的那个数啦(是所有数出现三次而只有一个出现1次或者只有一个出现2次)。
2.这类题还有一个变种,就是说,所有数出现两次,有两个数出现1次。怎么求呢?先异或一遍得到一个数num,然后分成两队再分别异或,通过num最右边第一个出现的1的那个二进制位可以划分啦!
3.一个数组,1<=a[i]<=n,n是数组长度,有的数出现两次有的数出现1次,怎么找出出现两次的所有数?O(n) time, O(1)space.
思路:法一:找特点,数组下标是0~n-1,数组内容是1~n,有重复,
for(int i=0; i<arr.size(); i++){
while(arr[i] != arr[arr[i]-1]){
swap(arr[i], arr[i]);
}
}
这样就可以出现一次的数字比下标大1,如果不满足,一定是重复数字了。
法二:既然不能申请额外空间,那就原地当哈希表,
for(int i=0; i<arr.size(); i++){
int index = arr[i]-1;
if(arr[index] > 0){
arr[index] = 0-arr[index];
}else{
res.push_back(index);
}
}
如果再次碰见负数说明这个下标位置已经找过一次了,重复。利用的是数组下标唯一并且数字范围的特点。