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

 

 

 思路1:

  运用HashSet中元素不重复的性质,计算sumset = sum(set(nums)),与sumnum = sum(nums)

  (sumset*3 - sumnum)/2即得到结果。

原理:

  设所求数字为 x,其他数字和为sumy,则sumset == x+(sumy/3),sumnum == x+sumy,

  因此3*sumset == 3*x+sumy,sumset*3 - sumnum == 2*x

代码:

class Solution {
    public int singleNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        long sumset = 0;
        long sumnum = 0; 
        for (int x : nums) {
            sumnum += x;
            if (set.contains(x)) {
                continue;               
            }  
            sumset += x;
            set.add(x);
        }
        return (int)((3 * sumset - sumnum) / 2);
    }
}

 

思路2:(剑指offer书上思路)

  位运算+遍历统计

原理:

  对于出现三次的数字,其各二进制位出现的次数都是 3 的倍数

  因此,统计所有数字的各二进制位中 1 的出现次数,并对 3 求余,结果则为只出现一次的数字。

实现:

  通过与运算可以得到数字 num 的二进制最后一位 ,再进行无符号右移操作,可以获取数字 num 二进制所有位的值。

  建立一个长度为 32 的数组 counts ,通过以上方法可记录所有数字的各二进制位的 1的出现次数。

  将 counts 各元素对 3 求余,结果为 “只出现一次的数字” 的各二进制位。

  再通过左移和或运算,可将 counts 数组中各二进位的值恢复到所求数字 res 上。

PS:这是一个通用解法,只要修改求余数值 m ,即可实现解决除了一个数字以外,其余数字都出现 m 次的问题。

代码:

class Solution {
    public int singleNumber(int[] nums) {
        int[] counts = new int[32];
        for(int num : nums) {
            for(int j = 0; j < 32; j++) {
                counts[j] += num & 1;
                num >>>= 1;
            }
        }
        int res = 0, m = 3;
        for(int i = 0; i < 32; i++) {
            res <<= 1;
            res |= counts[31 - i] % m;
        }
        return res;
    }
}


参考:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-ii-lcof/solution/mian-shi-ti-56-ii-shu-zu-zhong-shu-zi-chu-xian-d-4/
来源:力扣(LeetCode)
posted @ 2021-02-25 00:44  zjcfrancis  阅读(46)  评论(0编辑  收藏  举报