[剑指-Offer] 56 - I. 数组中数字出现的次数及II. 数组中数字出现的次数 II(位运算、异或、顶级解法)

1. 题目来源

链接:I. 数组中数字出现的次数
链接:II. 数组中数字出现的次数 II
来源:LeetCode——《剑指-Offer》专项

2. 题目说明

在这里插入图片描述
在这里插入图片描述

3. 题目解析 — I. 数组中数字出现的次数

方法一:位运算+异或+顶级解法

起初看这个问题并没什么思路,要求 O ( 1 ) O(1) 的空间 O ( n ) O(n) 的时间,注意到重复出现这个特点,猛地想起了 异或,但是得把这两个不同数字变成一个不同数字才可以直接拿异或解题。那么问题的关键就是分离这两个不同的数,将该问题分解为有两个不同的数在两个数组中这个数字单独出现,其余数字均只出现两次,求解该数的问题。那么直接异或即可,相同的数异或为 0,留下来的就是待确定的数了。

解决方法如下:

  • 还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。 因为其他数字都出现了两次,在异或中全部抵消了。由于这两个数字肯定不一样,那么异或的结果肯定不为 0,也就是说在这个结果数字的二进制表示中至少就有一位为 1。在结果数字中找到第一个为 1 的位的位置,记为第 n 位。现在我们以第 n 位是不是 1 为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第 n 位都是 1,而第二个子数组中每个数字的第 n 位都是 0。
  • 由于分组的标准是数字中的某一位是 1 还是 0,那么出现了两次的数字肯定被分配到同一个子数组。因为两个相同的数字的任意一位都是相同的,不可能把两个相同的数字分配到两个子数组中去,于是已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他
    数字都出现了两次。

至此所有的问题都已经解决了。

参见代码如下:

// 执行用时 :32 ms, 在所有 C++ 提交中击败了42.14%的用户
// 内存消耗 :17.7 MB, 在所有 C++ 提交中击败了100.00%的用户

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int tmp = 0;
        for (int i = 0; i < nums.size(); ++i) {
            tmp ^= nums[i];
        }
        int index = 0;
        while (((tmp & 1) == 0) and (index < 8 * sizeof(int))) {
            tmp = tmp >> 1;
            ++index;
        }
        int a = 0, b = 0;
        for (int j = 0; j < nums.size(); ++j) {
            if ((nums[j] >> index) & 1) a ^= nums[j];
            else b ^= nums[j];
        }
        return {a, b};
    }
};

4. 题目解析 — II. 平衡二叉树

方法一:位运算+顶级解法

如果一个数字出现三次,那么它的二进制表示的每一位也出现三次。如果把所有出现三次的数字的二进制表示的每一位都分别加起来,那么每一位的和都能被 3 整除。若该位能被三整除,则只出现一次的数字的二进制位上数字为 0,否则,数字为 1。

位运算…服了…

话说题解区的大佬推导的状态机,数字电路基础的知识,确实也是够顶了~

扫描二维码关注公众号,回复: 9746148 查看本文章

参见代码如下:

// 执行用时 :60 ms, 在所有 C++ 提交中击败了52.03%的用户
// 内存消耗 :17.9 MB, 在所有 C++ 提交中击败了100.00%的用户

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        vector<int> vt(32, 0);
        for (int i = 0; i < nums.size(); ++i) {
            for (int j = 0; j < 32; ++j){
                vt[j] += nums[i] & 1;
                nums[i] >>= 1;
            }
        }
        int res = 0;
        for (int i = 0; i < 32; ++i){
            if (vt[i] % 3 == 1) res += 1 << i;
        }
        return res;
    }
};
发布了343 篇原创文章 · 获赞 197 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/yl_puyu/article/details/104759781
今日推荐