剑指 Offer 56 - I. 数组中数字出现的次数

剑指 Offer 56 - I. 数组中数字出现的次数
剑指 Offer 56 - I. 数组中数字出现的次数
最容易想到的办法自然是哈希计数,但是我们发现题目的范围给到了\(1e5\),不断的给哈希表扩容比较花时间,也需要\(O(n)\)的遍历时间,还需要开哈希表的\(O(n)\)空间。

class Solution {
    public int[] singleNumbers(int[] nums) {
        int[] res = new int[2];
        Map<Integer, Integer> map = new HashMap<>();
        for(var num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        int idx = 0;
        for(var key : map.keySet()) {
            if(map.get(key) == 1) {
                res[idx++] = key;
            }
        }
        return res;
    }
}

再接着考虑原地空间的方式,这时就需要注意到其他数字都出现了2次,由异或的知识可以知道\(n \text{^} n = 0\)且\(0 \text{^} n = n\)。
因此我们先枚举一趟\(nums\),从\(0\)开始异或每一个\(num\),如果出现过两次的就会相互消去,只出现过1次的那两个数字a,b得到了它们异或的结果\(n = a \text{^} b\)。
此时我们还是无法确定\(a\)和\(b\)的,需要再想到我们求得\(n\)的最右边那位1设这位索引位置为\(i\),得到此时的值\(n = n \text{&} (-n)\)。
可以比较容易的得到\(a_2\)的第\(i\)位和\(b_2\)的第\(i\)位显然一个为1,一个为0,因此总能得到a和b。

class Solution {
    public int[] singleNumbers(int[] nums) {
        int n = 0;
        for(var num : nums) {
            n ^= num;
        }
        n &= -n;
        int[] res = new int[2];
        for(var num : nums) {
            if((num & n) != 0) {
                res[0] ^= num;
            } else {
                res[1] ^= num;
            }
        }
        return res;
    }
}
上一篇:springcloud面试题


下一篇:Java集合- HashMap 的7种遍历方式