1. 集合增量问题
一组大小为1到n的牌,抽走一张,求抽走的是哪张牌?
此类问题特点:
输入:1)已知原始集合:或者是直接给定一个数组,或者是给一个描述,比如1到n,每个数出现一次(或2次),总之原始集合是确定的
2)改变后的集合:原始集合拿走(添加)一个或2个数之后的集合
求:少了的数,多出来的数等,其实就是求两个集合的增量
思路:题目所求就是求2个集合之间的增量(差异),所以思路就是“求差”,对原始集合求和,减去当前集合的和,就是多出来或者缺失的数;如果是少了2个数怎么办?多算一种差异,平方和的差,联列x+y= a, x^2+y^2 = b 解方程。理论上几个数都可以,既增加数,又减少数也可以,因为都是解方程,就是变量个数和方程形式的问题。
少了2个数,多了2个数,少了一个数,同时一个数多出现了一个,少了一个数,多了一个数(这个数不属于集合),这些问法都是一样的。
如果元素是连续的,可以映射到下标,则可以用桶排序思想
大小为n的数组,元素是1到n的数,但是少了一个数,同时一个数多出现了一次,求这两个数。
桶排序,尝试把每个数组元素a[i] 放在它应该出现的位置,如果那个位置已经是这个数了,说明这个数重复了;i++。最后从前往后再扫一遍,第一个位置上不是对应数的位置就是缺失的数。
2. Single number 问题,其他数都出现了k(k>1)次,只有一个数出现了一次,求这个数
分析:如果原始集合是已知的,依然可以使用2集合求差异的方法,(原始和 - 实际和 ) /( k-1) 即为所求。但这里原始集合不是给定的,需要统计unique的数,需要一个set,那还不如用最基本的map计数法。
换一种思路:考虑把出现了多次数消掉,如果k是偶数,则可用异或法;如果k是奇数,则可以用求和再求余法,出现k次的数模k为0,和里最终剩下的是那个single number %k,还是不行,再利用一个性质,小于k的数对k取模就是这个数本身,分别求single number的每一位
int singleNumber(vector& nums) {
int ans = 0;
for (int i = 0; i < 32; ++i) {
int b = 0;
for (int x: A) {
b += (x >> i) & 1;
}
ans |= (b % 3) << i;
}
return ans;
}
3 另一种single Number问题,但变化的是single number的个数
一般的数都出现2次(或者偶数次)只有k个数只出现一次。求这k个只出现一次的数。如果k = 1,直接异或法;如果k=2,先异或,找一个为1的bit位,按这一位是0还是1把所有的数分成2部分,两个出现一次的数分别在这两部分,然后各自异或法求。
如果k = 3。首先要明确3点:1)数组总数一定是奇数,2)3个不同的数异或结果有可能是0,3)两个不同的数异或必然不为0。
按第0位是0还是1分成两组,必然一个组个数是奇数一个组个数是偶数。这三个数中,偶数组要么有2个,要么没有,不能有3个或1个。所以偶数组异或为0,则说明三个数都在奇数组,递归问题。若偶数组异或不为0,则说明有两个在其中,异或奇数组得到一个数。偶数组变成k= 2的 single Number问题。