其他数出现3次、一个数出现1次,求这单个数

int singleNumber(vector<int>& nums) {
    // 泛化成:其他数出现k次,某个数出现p次(p不能被k整除),找这单个数
    // bit运算各个位相互独立,下面用int同时操作所有位,逻辑上把整个int当作一个bit(nums[i]也当作一个bit)
    // 要统计某位上出现1的次数,需要个mod k计数器(该计数器共m>=lgk位),设bm,..,b2,b1表示该计数器的位,
    // 1. 进位规则:从最高位开始更新,后面位和新数num都为1时进位
    //    ..., b3^=b2&b1&num, b2^=b1&num, b1^=num
    // 2. 计数为k时直接清零:
    //    当bm,..,b1的各个位分别等于k的各个位km,..,k1时计数为k,判断计数为k的表达式
    //    为expr_count_k=(bm_km)&..&(b1_k1)。分量(bj_kj)表示于kj==1时取bj、kj==0时取~bj
    //    清零的 mask = ~(expr_count_k),清零:..., b3&=mask, b2&=mask, b1&=mask
    // 3. 最终各逻辑位”合并或“:b1|b2|b3...
    int b1 = 0, b2 = 0;
    int mask = 0;
    for (int num : nums) {
        b2 ^= b1 & num;
        b1 ^= num;
        mask = ~(b1 & b2); // k =3 =(11)_2
        b2 &= mask;
        b1 &= mask;
    }
    return b1 | b2;
}

还有种方法设计mod 3计数器:用数电知识,画出x1,x2,nums[i]的卡诺图真值表、化简x1,x2表达式。只是该方法泛化能力差,计数器位数多了以后表达式复杂。

其他数出现2次,两个数出现1次,求这两个数

vector<int> singleNumber(vector<int>& nums) {
    // 设x和y是数组中只出现1次的两个数
    int xXorY = 0;
    for (auto num : nums) {
        xXorY ^= num;
    }
    // xXorY中任意一位1表示x和y该处不同,不妨取最后一位1来区分x和y
    int lastOne = xXorY & -xXorY;
    int x = 0;
    for (auto num : nums) {
        if (num & lastOne) x ^= num;
    }
    int y = x ^ xXorY;

    return {x, y};
}

有序数组中其他数出现两次、某个数出现一次,求这单个数

int singleNonDuplicate(vector<int>& nums) {
    // 由题知N是奇数,对于[0..N-2]的一对对,找第一对nums[m]!=nums[m^1]
    // nums[m]!=nums[m^1]作二分搜索条件满足[0 ... 0 1 1 ...]形式
    int l = -1, u = (int)nums.size() - 1;
    while (l + 1 < u) {
        int mid = l + (u - l) / 2;
        if (nums[mid] != nums[mid ^ 1]) u = mid;
        else l = mid;
    }
    return nums[u];
}