异或运算妙用

在一组整数中,若有两个数字各自出现奇数次,其他数字均出现偶数次,如何高效地找出这两个数字?

一种常用且高效的方法是利用异或运算

异或运算的性质:

  1. 自反性a ^ a = 0,即相同的数字异或结果为0。
  2. 交换律和结合律a ^ b = b ^ aa ^ (b ^ c) = (a ^ b) ^ c,即运算顺序和位置可以任意调整。
  3. 与0的运算a ^ 0 = a,即任何数字与0异或结果为其本身。

基于这些性质,我们可以设计如下算法:

算法步骤:

  1. 对所有数字进行异或运算:遍历数组中的每个数字,将其与一个累积变量(初始值为0)进行异或操作。由于相同的数字出现偶数次,其异或结果为0,最终累积变量的值将是两个只出现奇数次的数字的异或结果。

  2. 从异或结果中提取信息:由于异或结果不为0,说明至少有一位二进制位为1。我们可以通过以下方式获取最右侧的1所在的位:

    int rightmostOne = xorResult & (-xorResult);

    这里,-xorResultxorResult 的二进制补码,xorResult & (-xorResult) 的结果是 xorResult 中最右侧的1所在的位。

  3. 根据该位将数组分为两组:遍历数组,将每个数字根据其在该位上的值(0或1)分配到两组中。

  4. 分别对两组进行异或运算:对每组中的数字分别进行异或操作,最终得到的结果即为两个只出现奇数次的数字。

Java实现示例:

public class FindOddNumbers {
    public static void findOddNumbers(int[] arr) {
        int xorResult = 0;
        // 第一次遍历:对所有数字进行异或操作
        for (int num : arr) {
            xorResult ^= num;
        }

        // 获取 xorResult 最右侧的 1
        int rightmostOne = xorResult & (-xorResult);

        int num1 = 0, num2 = 0;
        // 第二次遍历:根据 rightmostOne 将数组分为两组,分别对每组进行异或操作
        for (int num : arr) {
            if ((num & rightmostOne) != 0) {
                num1 ^= num;
            } else {
                num2 ^= num;
            }
        }

        System.out.println("出现奇数次的两个数字分别是: " + num1 + " 和 " + num2);
    }

    public static void main(String[] args) {
        int[] arr = {1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8};
        findOddNumbers(arr);
    }
}
输出:
出现奇数次的两个数字分别是: 4 和 7

时间和空间复杂度:

  • 时间复杂度:O(n),其中n是数组的长度。我们只需要遍历数组两次。
  • 空间复杂度:O(1),只使用了常数空间。

通过上述方法,我们可以在O(n)的时间复杂度和O(1)的空间复杂度下,找出数组中两个只出现奇数次的数字。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值