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

    技术2022-07-11  81

    前言:

    里面提到了异或运算:

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

    在这里补一下。

    异或,英文为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/

     

    Processed: 0.011, SQL: 9