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

 

原理:位运算

  如果该整型数组中只有一个数字 x 出现一次,其他数字出现两次。

  由于两个相同的数字异或后得0,若将 nums 中所有数字执行异或运算,留下的结果则为出现一次的数字 x。

思路:

  题目中有两个数字只出现一次,nums 中所有数字执行异或运算后得到 x⊕y。

  由于x≠y,则 x⊕y 二进制位中有至少一位为 1,可以根据这一位将 nums 分为两个子数组,分别包含 x 和 y。

  这样两个子数组的所有数字分别进行异或运算,则可以得到 x 与 y。

  根据与运算的特点,可以初始化一个辅助变量 m = 1,通过与运算从右向左循环判断,可获取 xy 二进制位中第一位 1。

  通过遍历判断 nums 中各数字和 m 做与运算的结果,可将数组拆分为两个子数组。

class Solution {
    public int[] singleNumbers(int[] nums) {
        int x = 0, y = 0, n = 0, m = 1;
        for(int num : nums)                 // 1. 遍历异或,得到x⊕y
            n ^= num;
        while((n & m) == 0)                 // 2. 循环左移,计算 m
            m <<= 1;
        for(int num: nums) {                // 3. 分组
            if((num & m) != 0) 
                x ^= num;                   // 4. 当 num & m != 0,即 x⊕y 二进制位中第一位 1 处num != 0
            else 
                y ^= num;                   // 4. 即……num == 0
        }
        return new int[] {x, y};            // 5. 结果
    }
}

注意:

  if((num & m)!=0)这里不能写成 if((num & m)!=1)

  因为 num & m 的结果不只有 0 和 1,如 2 & 2 = 2(10 & 10 = 10)

  如果写成!=1,会将非0的子数组切割一部分进等于0的子数组,可能导致x、y均出现在一个子数组中,结果错误。

posted @ 2021-02-25 00:06  zjcfrancis  阅读(42)  评论(0编辑  收藏  举报