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

给定一个整型数组,其中大部分元素出现两次,但有两个元素只出现一次。使用异或位运算在O(n)时间和O(1)空间复杂度下找出这两个元素。通过全员异或,再利用数字的奇偶性进行分组,最后对两个不同组进行异或操作得到答案。
摘要由CSDN通过智能技术生成

一、题目

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

示例 1:

输入:nums = [4,1,4,6]
输出:[1,6][6,1]

示例 2:

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10][10,2]

限制:

2 <= nums.length <= 10000

二、解决

1、异或位运算

思路:

简单版:除了一个数字,其他数字都出现了两次,那如何找到出现一次的数字?
解决:全员异或。相同的数字,异或一下会两两抵消。

进阶版:如何找出两个出现一次的数字?
解决-关键:分组

  1. 两个只出现一次的数字在不同的组中
  2. 相同的数字会被分到相同的组中

解决:对这两个组分别进行异或操作即可得到答案的两个数字。

思考:如何分组呢?

  1. 重复的数字进行分组。简单,可以用奇偶性,数值相同的一定会分到同一组。
  2. 难点:如何对两个不同数字进行分组。
  3. 方案:可以用 & 1 操作。也相当于奇偶性分组(不过这里不一定是最后一位,而是全员异或后,倒数第一个二进制为1),即判断最后一位二进制是否为1,判断数记为mask。

举例:

例子14 ^ 1 ^ 4 ^ 6-->
40100
10001
-------
=  0101
40100
-------
=  0001
60110
-------
=  0111

1^6-->
60110
10001
-------
=  0111

0001可以用来分组。
mask:0001


例子24 ^ 8 ^ 4 ^ 6-->
40100
81000
-------
=  1100
40100
-------
=  1000
60110
-------
=  1110

8^6-->
60110
81000
-------
=  1110

0010可以用来分组。
mask:0010

代码:

class Solution {
    public int[] singleNumbers(int[] nums) {
        //用于将所有的数异或起来
        int k = 0;
        
        for(int num: nums) {
            k ^= num;
        }
        
        //获得k中最低位的1
        int mask = 1;

        //mask = k & (-k) 这种方法也可以得到mask,具体原因百度 哈哈哈哈哈
        while((k & mask) == 0) {
            mask <<= 1;
        }
        
        int a = 0;
        int b = 0;
        
        for(int num: nums) {
            if((num & mask) == 0) {
                a ^= num;
            } else {
                b ^= num;
            }
        }
        
        return new int[]{a, b};
    }
}

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

// k & (-k)
// 原码、反码、补码
// 正数:三码相同
// 负数
// 举例:-5 
// 原码:1..101  (符号位置1)
// 反码:1..010  (符号位不变,其他取反)
// 补码:1..011  (反码+1)
System.out.println(Integer.toBinaryString(5)+" " );
System.out.println(Integer.toBinaryString(-5)+" ");
System.out.println(Integer.toBinaryString(5^(-5)));
00000000000000000000000000000101 
11111111111111111111111111111011
11111111111111111111111111111110

三、参考

1、接地气讲解(分组位运算)「还不懂就来P城砍我」
2、数组中数字出现的次数
3、[译]你必须知道的基本位运算技巧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值