剑指 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)