算法 | 基础 | 出现奇数次的数字

这里写自定义目录标题

本篇是关于异或(^)运算的运用。后期看算法过程中如果再碰到异或的都会收录到本篇中

异或运算

在逻辑学中,逻辑算符异或(exclusive or)是对两个运算元的一种逻辑析取类型,符号为 XOR 或 EOR 或 ⊕(编程语言中常用^)。但与一般的逻辑或不同,异或算符的值为真仅当两个运算元中恰有一个的值为真,而另外一个的值为非真。转化为命题,就是:“两者的值不同”或“有且仅有一个为真”

true ^ true = false
false ^ false = false
true ^ false = true
同理
1 ^ 1 = 0
0 ^ 0 = 0
1 ^ 0 = 1

且满足两个特性:交换律、结合律、归零律、恒等律

交换律:A ^ B = B ^ A
结合律:(A ^ B) ^ C = A ^ (B ^ C)
归零律:A ^ A = 0
恒等律:A ^ 0 = A

有了归零率和结合律,我们就可以轻松证明:

自反:A ^ B ^ B   =    A ^ 0   =    A

题目1

题目:给定一个数组,其中只有一个数出现奇数次,其他都出现偶数次,打印奇数次的数。
解法:结合律和归零律
例子int[] array = {1, 2, 2, 3, 3, 4, 4, 5, 5} 输出 1
思想

   a ^ b ^ c ^ a ^ d ^ b ^ c ^ d ^ e ^ e ^ e ^ f ^ f 
=  a ^ a ^ b ^ b ^ c ^ c ^ d ^ d ^ e ^ e ^ e ^ f ^ f //分组
=  0 ^ 0 ^ 0 ^ 0 ^ 0 ^ 0 ^ e ^ 0 //两两归零
=  e

代码

public class FindOddOccurrenceNumberUsingXOR {
    public static int findOddNumber(int[] arr) {
        int result = 0;
        for (int num : arr) {
            result ^= num;
        }
        return result;
    }

    public static void main(String[] args) {
        int[] array = {1, 2, 2, 3, 3, 4, 4, 5, 5};
        int oddNumber = findOddNumber(array);
        System.out.println("奇数个的数字是:" + oddNumber);
    }
}

题目2

题目:给定一个数组,其中有两个数出现奇数次,其他都出现偶数次,打印奇数次的数。
解法:结合律和归零律
例子int[] array = {1, 2, 2, 3, 3, 3, 4, 4, 5, 5} 输出 1,3
思想

   a ^ b ^ c ^ b
=  a ^ 0 ^ c //两两归零
=  a ^ c
=  x

知道这两个值异或的结果,如何知道两个值呢?自反
A ^ B ^ B = A
对于X转成二进制后,我们知道肯定有一位是1,因为x = a ^ c那么这一位为1的数不是a就是c仅为其中1个,我们假设是a。那么对于整个数组所有数来说,就分了两个阵营。

该位置为1该位置为0
ac
de,n,f

最后一步判断:因为其他数字都是偶数个,所以^后都是0,只有基数个的数字c^才是1。这样就能获取到a和c的值了。

代码

public class FindTwoOddOccurrenceNumbers {
    public static int[] findTwoOddNumbers(int[] arr) {
        int xorResult = 0;
        for (int num : arr) {
            xorResult ^= num;
        }
        //xorResult最终的值是 5^1

        // 找到两个奇数个数字的异或结果中的某一位为 1 的位置,讲解如下:
        // 假如 xorResult = 5 = 101
        // 减1取反后:~(101-001) = 011
        // 再和101取&= 001;
        // 此种算法就是获取一个数的最右一位的1
        int rightmostSetBit = xorResult & (~(xorResult - 1));

        int firstNumber = 0;
        for (int num : arr) {
        	//获取与(其中一个基数个数字)同位置都是1的数字
            if ((num & rightmostSetBit)== 1) {
            	//其他数字都是偶数个,都是0,所以可以找到(其中一个基数个数字)
                firstNumber ^= num;
            }
        }
        //自反原则,xorResult ^ firstNumber = (5^1)^1 = 5;
        return new int[]{firstNumber, xorResult ^ firstNumber};
    }

    public static void main(String[] args) {
        int[] array = {4, 2, 4, 5, 2, 3, 3, 1};
        int[] result = findTwoOddNumbers(array);
        System.out.println("两个出现奇数次的数字是:" + result[0] + " 和 " + result[1]);
    }
}
  • 17
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值