只出现一次的数字

0 阅读3分钟

位运算 + 按位统计来求「数组中只有一个数字出现一次,其它数字都出现三次」的问题

默认是处理非负数(positive number)

解释逻辑:

  • 数组里除了一个数只出现一次,其余数字都出现三次
  • 对每个二进制位统计 1 出现的总次数
  • 最后对 3 取模(% 3)只剩下出现一次的那个数在该位上的贡献

int[] nums = {2, 2, 3, 2}; 含义:

  • 数组里:数字 2 出现了三次
  • 数字 3 出现了一次

题目保证:除了一个数只出现一次,其余数都出现三次

为什么一定要出现恰好 k 次?

因为:

  • 取模的原理依赖于「其它数的贡献都是 k 的倍数」
  • 如果其它数出现了非 k 次,比如 2 次、4 次,就不能保证「其它数贡献的 1 总数 % k == 0」

(num >> (31 - i)) & 1二进制右移找1

解释:

  • num >> (31 - i):把 num 的二进制右移到第 i 位移到最低位
  • & 1:只看最低位是 1 还是 0

得到结果是:

  • 第 i 位是 1:加 1
  • 第 i 位是 0:加 0

下面是 i 从 0 到 3 时的流程示意:

i31 - inum >> (31-i)得到的最低位bitSums[i] +=
031000...00000000最低位是 0加 0
130000...00000000最低位是 0加 0
...............
301000...00000001最低位是 1加 1
310000...00000011最低位是 1加 1

统计 bitSums(每个位上 1 出现的次数)

bitSums 数组有 32 个元素,但只看最后两位(i=30 和 i=31)

i(31 - i)表示第几位每个数贡献bitSums[i]
301倒数第2位2:1,2:1,3:1,2:1 → 共4个14
310最低位2:0,2:0,3:1,2:0 → 共1个11

+ bitSums[i] % 3

  • 统计的次数对 3 取模,只会是 0 或 1
  • 如果是 1:说明这一位只出现一次的数字在该位是 1
  • 如果是 0:说明该位只出现一次的数字是 0
public class A_SingleNumber {
    public int singleNumber (int [] nums) {//输入:一个整型数组 `nums`
        int [] bitSums = new int[32];//创建了一个长度为 32 的数组
        for (int num : nums) {
            for (int i = 0; i < 32; i++) {
                bitSums[i] += (num >> (31-i)) & 1;
            }
        }
        int result = 0;
        for (int i = 0; i < 32; i++) {
            result = (result << 1) + bitSums[i] % 3;//当前结果左移一位,准备在最低位加上当前位
        }
        return result;//返回计算出的只出现一次的数字
    }

    public static void main(String[] args) {
        A_SingleNumber obj = new A_SingleNumber();
        int[] nums = {2, 2, 3, 2};
        int ans = obj.singleNumber(nums);
        System.out.println("Single number is: " + ans);
    }
}

i=0 到 i=29:

bitSums[i]=0 → bitSums[i]%3=0
每次:

  • result<<1 → result 还是 0
  • +0 → result 还是 0

所以 result 还是 0

i=30:

  • bitSums[30]=4
  • 4%3=1 → 当前位是1
  • result=0
  • result<<1=0
  • +1 → result=1

i=31:

  • bitSums[31]=1
  • 1%3=1 → 当前位是1
  • result=1
  • result<<1=2
  • +1 → result=3

最后得到:

  • result=3
    说明数组里只出现一次的数字是:3

唯独只出现一次的数字,它的1在那一位上会多出来 截屏2025-07-02 13.55.28.png 必须规定:

  • 除了一个数只出现一次
  • 其它数都出现恰好 k 次(比如 3 次)

才能通过「按位统计 1 次数,对 k 取模」来找出只出现一次的数

补充:从左到右四位:

ibitSums[i]%3result计算过程
01result=0<<1+1=1
10result=1<<1+0=2
21result=2<<1+1=5
31result=5<<1+1=11

4%3=1

4 ÷ 3 = 1 ...... 1

1%3=1

1 ÷ 3 = 0 ...... 1