面试题56 - I. 数组中数字出现的次数

题目:

面试题56 - I. 数组中数字出现的次数
260. 只出现一次的数字 III
在这里插入图片描述

题解:

题解1:分组异或

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意:

x & -x 表示的含义是:获得 x 中最低位的1

题解2:位运算+循环左移(优选)

class Solution {
    public int[] singleNumbers(int[] nums) {
        //因为相同的数字异或为0,任何数字与0异或结果是其本身。
        //所以遍历异或整个数组最后得到的结果就是两个只出现一次的数字异或的结果:即 z = x ^ y
        int z = 0;  
        for(int i : nums) z ^= i;
        //我们根据异或的性质可以知道:z中至少有一位是1,否则x与y就是相等的。
        //我们通过一个辅助变量m来保存z中哪一位为1.(可能有多个位都为1,我们找到最低位的1即可)。
        //举个例子:z = 10 ^ 2 = 1010 ^ 0010 = 1000,第四位为1.
        //我们将m初始化为1,如果(z & m)的结果等于0说明z的最低为是0
        //我们每次将m左移一位然后跟z做与操作,直到结果不为0.
        //此时m应该等于1000,同z一样,第四位为1.
        int m = 1;
        while((z & m) == 0) m <<= 1;
        //我们遍历数组,将每个数跟m进行与操作,结果为0的作为一组,结果不为0的作为一组
        //例如对于数组:[1,2,10,4,1,4,3,3],我们把每个数字跟1000做与操作,可以分为下面两组:
        //nums1存放结果为0的: [1, 2, 4, 1, 4, 3, 3]
        //nums2存放结果不为0的: [10] (碰巧nums2中只有一个10,如果原数组中的数字再大一些就不会这样了)
        //此时我们发现问题已经退化为数组中有一个数字只出现了一次
        //分别对nums1和nums2遍历异或就能得到我们预期的x和y
        int x = 0, y = 0;
        for(int i : nums) {
            //这里我们是通过if...else将nums分为了两组,一边遍历一遍异或。
            //跟我们创建俩数组nums1和nums2原理是一样的。
            if((i & m) == 0) x ^= i;
            else y ^= i;
        }
        return new int[]{x, y};
    }
}

代码:

代码1:分组异或

/**
 * 面试题56_1
 */
public class 面试题56_1 {

    public static int[] singleNumbers(int[] nums) {
        int sum = 0;
        int res[] = new int[2];
        // sum用于将所有的数异或起来
        for(int i = 0; i < nums.length; i++)
        {
            sum  = sum ^ nums[i];
        }
        // 获得sum中最低位的1
        int lowbit = sum & (-sum);

        for(int i = 0; i < nums.length; i++)
        {
            if((nums[i] & lowbit) == 0) // 如果该位为0,就分到第一组
            {
               res[0] = res[0] ^ nums[i]; 
            }
            else                        // 如果该位不为0,就分到第二组
            {
                res[1] = res[1] ^ nums[i];
            }
        }
        return res;
    }

    public static void main(String[] args) {
        int nums[] = { 1,2,10,4,1,4,3,3 };
        int res[] = singleNumbers(nums);
        for(int i = 0; i < res.length; i++)
        {
            System.out.print(res[i] + " ");
        }
    }
}

代码2:位运算+循环左移(优选)

public class 面试题56_1 {

    public static void main(String[] args) {
        int nums[] = {1, 2, 10, 4, 1, 4, 3, 3};
        int res[] = sockCollocation(nums);
        for (int i = 0; i < res.length; i++) {
            System.out.print(res[i] + " ");
        }
    }

    public static int[] sockCollocation(int[] sockets) {
        int z = 0;
        for (int i = 0; i < sockets.length; i++) { // 遍历 sockets 执行异或运算
            z ^= sockets[i];
        }
        int m = 1;
        while ((z & m) == 0) { // m 循环左移一位,直到 z & m != 0
            m <<= 1;
        }
        int x = 0;
        int y = 0;
        for (int i = 0; i < sockets.length; i++) {
            if ((sockets[i] & m) == 0) { // 若 num[i] & m == 0 , 划分至子数组1 ,执行遍历异或
                x ^= sockets[i];
            } else {                     // 若 num[i] & m != 0 , 划分至子数组2 ,执行遍历异或
                y ^= sockets[i];
            }
        }
        int res[] = new int[] {x, y}; // 遍历异或完毕,返回只出现一次的数字 x 和 y
        return res;
    }
}

参考:

  1. 数组中数字出现的次数
  2. 【只出现一次的数系列】位运算
  3. 接地气讲解(分组位运算)「还不懂就来P城砍我」
  4. 什么?这题还可以用二分查找?🤷‍♀️必须秒懂!
  5. 只出现一次的数字 III
  6. 编程小记—— C/C++中 x & -x 表示含义
  7. LCR 177. 撞色搭配(位运算,清晰图解)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dev_zyx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值