[算法]leetcode 剑指offer数组中数字出现的次数

前言:

里面提到了异或运算:

异或运算是什么,我真是不怎么知道。

在这里补一下。

异或,英文为exclusive OR,缩写成xor

它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。其运算法则为:

a⊕b = (¬a ∧ b) ∨ (a ∧¬b)

如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。

0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0

 

让我们先来考虑一个比较简单的问题:

如果除了一个数字以外,其他数字都出现了两次,那么如何找到出现一次的数字?

答案很简单:全员进行异或操作即可。考虑异或操作的性质:对于两个操作数的每一位,相同结果为 00,不同结果为 11。那么在计算过程中,成对出现的数字的所有位会两两抵消为 00,最终得到的结果就是那个出现了一次的数字。
a^a=0
a^0=a

 

接下来是&运算

运算规则:

0&0=0;0&1=0;1&0=0;1&1=1

即:两个同时为1,结果为1,否则为0

例如:3&5

十进制3转为二进制的3:0000 0011

十进制5转为二进制的5:0000 0101

 

那么这个思路也就是接下来找到,两个单独数字的思路了~

题目:

一个整型数组 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

解题思路:

4 ^ 1 ^ 4 ^ 6 => 1 ^ 6

6 对应的二进制: 110
1 对应的二进制: 001
1 ^ 6  二进制: 111
此时我们无法通过 111(二进制),去获得 110 和 001。

 

那么我们如果能够把这两个数分开组就好了~。

那么怎么分组呢?

因为两个数,二进制的话,至少有一位不一样,

根据上图,进行异或运算。

最终的得到的数字 1 ^ 6  二进制: 111 (不一样有值,那么下面用最低位 001其实他们两个就是不一样的)

相当于一个mask(用于分组)。

那么根据这个mask的最低位,我们就可以做与运算的时候

比如说这两个数字,就是最低位是1,那么说明这两个数字

比如所有的数字跟001进行与运算的时候,如果最低位为1,那么就与运算就有值,否则没有,那么肯定能把这两个数字弄到不同的分组。

那么初始化一个a和b做两个数字的计数。

if ((num & mask) == 0) {
    a ^= num;
} else {
    b ^= num;
}

与运算与这个mask的时候,等不等于0就可以区分这两个数字了。

 


 

代码:

public class SingleNumbers {

    /**
     * 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。
     * 要求时间复杂度是O(n),空间复杂度是O(1)。
     * 输入:nums = [4,1,4,6]
     * 输出:[1,6] 或 [6,1]
     * <p>
     * 主要是对两个不同数字的分组
     * <p>
     * 奇偶运算:& 1 操作, 当我们对奇偶分组时,容易地想到 & 1,即用于判断最后一位二进制是否为 1。来辨别奇偶。
     */
    public int[] singleNumbers(int[] nums) {
        //用于将所有的数异或起来
        int k = 0;

        for (int num : nums) {
            k ^= num;
        }
        //获得k中最低位的1
        int mask = 1;

        //mask = k & (-k) 这种方法也可以得到mask
        //那么当k & mask != 0的时候,那么说明找到了这个不一样的一位了
        while ((k & mask) == 0) {
            //mask = mask << 1;
            mask <<= 1;
        }

        int a = 0;
        int b = 0;
        //这部分操作是什么呢?相当于循环一次数组
        //与运算只有都为1在某一位,才有值,否则都为0,那么这样一定会把这两个数分开。最后能求出来a和b
        for (int num : nums) {
            //mask与数字做异或
            if ((num & mask) == 0) {
                a ^= num;
            } else {
                b ^= num;
            }
        }
        return new int[]{a, b};
    }
}

 

感谢题解:

https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solution/jie-di-qi-jiang-jie-fen-zu-wei-yun-suan-by-eddievi/

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值