异或运算的算法题

先给出两道面试的算法题。

  • 一种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)
  • 两种种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)

这两道题的解法有很多,但是最快最简单的应该就是异或运算。

接下来我们需要了解什么是异或运算?

异或运算

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

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

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

异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。

总结一下,异或运算就是相同为0,不同为1。且A ^ A = 0 A ^ 0 = A 。同时满足交换律和结合律。

接下来开始解决我们之前提出来的两道算法题目。

题目一

一种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)

思路

  1. 由于它指明了空间复杂度为O(1),所以改题目不能额外创建数组,只能额外创建一些变量。
  2. 由上面的异或运算可知,A ^ A = 0 A ^ 0 = A。而改题目中恰好只有一个数字出现了奇数次。所以偶数次的数字直接消为0,最后只有奇数次的数字。

代码演示

/**
 * @version v1.0
 * @ProjectName: 数据结构
 * @ClassName: demo01
 * @Description: 一种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)
 *                两种种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)
 * @Author: 赵先生
 * @Date: 2022/2/9 10:25
 */

public class demo01 {
    /**
     * 一种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)
     * A ^ A = 0     A ^ 0 = A    满足交换律和结合律
     * @param arr
     */
    public static void printOddTimesNum1 (int[] arr) {
        int eor = 0;
        for (int cur : arr) {
            eor = cur ^ eor;
        }
        System.out.println(eor);
    }
}    

题目二

两种种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)

思路

  1. 我们先整体异或运算,这样子得要的是一个数是a ^ b,这里的ab是指为奇数次的这两个数。
  2. 然后我们提取eor中最右边的值,因为这个值就代表了a和b不同的那一位。(固定写法)
  3. 我们通过这个数字就可以将a和b分开,然后得要a或者b
  4. 最后通过得到的数和eor进行异或运算得到另外一位。

在这里插入图片描述
在这里插入图片描述
代码演示

/**
 * @version v1.0
 * @ProjectName: 数据结构
 * @ClassName: demo01
 * @Description: 一种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)
 *                两种种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)
 * @Author: 赵先生
 * @Date: 2022/2/9 10:25
 */

public class demo01 {
    
    /**
     * 两种种数出现了奇数次,其他数出现了偶数次,要求找到这个数,且时间复杂度为O(n),空间复杂度为O(1)
     * 思路:
     * 1,我们先整体异或运算,这样子得要的是一个数是a ^ b,这里的ab是指为奇数次的这两个数。
     * 2,然后我们提取eor中最右边的值,因为这个值就代表了a和b不同的那一位。
     * 3,我们通过这个数字就可以将a和b分开,然后得要a或者b
     * 4,最后通过得到的数和eor进行异或运算得到另外一位。
     * @param arr
     */
    public static void printOddTimesNum2(int[] arr) {
        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor ^= arr[i];
        }
        //eor = a ^ b(这里的a和b表示的是为奇数的这两个数)
        //eor != 0
        //eor必然有一个位置上是1,这个位置也就是a和b不相同的一个位置
        /**
         * 提取出最右边的1
         * 原理如下:
         * A 10000111
         * ~A 01111000
         * ~A+1 01111001
         * A & (~A+1) 00000001
         */
        int rightOne = eor & (~eor + 1);
        int onlyOne = 0;
        //通过下面的for循环我们就知道了a或者b的一个数,再通过这个数异或出另外一个数
        for (int cur : arr) {
            if ((cur & rightOne) == 1) {
                onlyOne ^= cur;
            }
        }
        System.out.println(onlyOne + " " + (eor ^ onlyOne));
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值