云计算百科
云计算领域专业知识百科平台

找两个单身狗(^的使用应用)(从右向左数第一个为 1 的比特位)

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

问题描述:

给定一个数组,其中除了两个数字之外,所有其他数字都出现了两次。我们的任务是找出这两个只出现一次的数字。

例如,给定数组:

csharp[1, 2, 3, 4, 5, 1, 2, 3, 4, 6]

其中,5 和 6 只出现了一次,其他数字都出现了两次。我们的目标是通过编程找出这两个数字。

解题思路:

1. 利用异或运算的性质

我们可以利用 异或运算 来解决这个问题。异或运算有以下几个重要的性质:

  • a ^ a = 0:两个相同的数字异或,结果是0。

  • a ^ 0 = a:任何数字与0异或,结果是它本身。

  • 异或运算是交换律和结合律的,意味着 a ^ b ^ c 与 a ^ c ^ b 结果相同。

由于数组中除了两个数字外,其它数字都出现两次,所以通过异或运算,最终我们可以将所有出现两次的数字都消掉,留下这两个只出现一次的数字。

2. 为什么异或所有数字能得到最终的结果?

假设我们有一个数组 [a, b, c, a, b, c, d, e],其中除了 d 和 e 外,所有数字都出现了两次。通过异或所有数字,我们得到以下结果:

nginx

a ^ b ^ c ^ a ^ b ^ c ^ d ^ e = (a ^ a) ^ (b ^ b) ^ (c ^ c) ^ d ^ e

由于任何数字与自己异或结果为0,所以 (a ^ a) = 0,(b ^ b) = 0,(c ^ c) = 0。因此最终结果是:

0 ^ 0 ^ 0 ^ d ^ e = d ^ e

这说明最终结果 d ^ e 是两个只出现一次的数字异或的结果。

3. 如何从 d ^ e 找到 d 和 e?

d ^ e 的结果告诉我们 d 和 e 在二进制上有不同的某一位(至少有一位是1)。通过分析 d ^ e 的结果,我们可以找出一个位,确保 d 和 e 在这一位上不同。然后,基于这个位,将数组分成两组,分别找出 d 和 e。

4. 实际操作步骤:
  • 第一步:计算数组中所有数字的异或结果,得到 d ^ e。

  • 第二步:找到 d ^ e 中任意一位为1的比特(这是 d 和 e 在该位置上的不同)。假设我们找到的是第 k 位。

  • 第三步:将数组中的所有数字根据该位是否为1分为两组:一组是该位为1的数字,另一组是该位为0的数字。

  • 第四步:分别对这两组数字做异或操作,最终得到 d 和 e。

详细步骤和代码实现:

第一步:计算所有数字的异或结果

我们首先对数组中的所有数字进行异或操作,得到 d ^ e。

for (int i = 0; i < n; i++)

{
        xorResult ^= arr[i];

 }

第二步:找到不同的比特位

接着,我们可以通过 d ^ e 的结果找到 d 和 e 在某一位上的差异。我们可以通过 d ^ e 的二进制表示来找到这一位:d ^ e & (-d ^ e)。

int rightmostBit = xorResult & (-xorResult); // 获取最右边的 1 位
这一行代码在位运算中非常有用,它的目的是 提取 xorResult 中最右边的 1 位,这在后续的步骤中帮助我们将数组中的数字分为两组,从而找出两个只出现一次的数字。

为什么需要提取最右边的 1 位?

通过 xorResult,我们已经知道数组中有两个数字 d 和 e 只出现一次,其余的数字都成对出现,异或的结果是这两个数字的异或。由于这两个数字一定在某些比特位上是不同的,利用这一点,我们可以将这两个数字分到不同的组中。为了分组,我们需要找出 xorResult 中某一位上的差异,而最简单的方法就是找到 最右边的 1 位。

如何理解 xorResult & (-xorResult):
  • xorResult 是这两个只出现一次的数字的异或结果。

          假设 xorResult 的值为 6,二进制是 0110。

  • – xorResult 取的是 xorResult 的 二进制补码:
    • 在计算机中,负数是通过补码表示的。补码是取反加 1。例如,6 的二进制是 0110,它的补码是:

      • 取反:0110 -> 1001

      • 加 1:1001 + 1 = 1010

    • 所以,-6 的二进制是 1010。

  • xorResult & (-xorResult):
    • 对 xorResult 和 -xorResult 进行按位与运算,可以提取出 xorResult 中最右边的 1 位。(是指在一个二进制数中,从右向左数第一个为 1 的比特位。)

    • 为什么呢?因为:

      • xorResult 中的 1 位,在补码 -xorResult 中会有一个对应的 1 位(补码表示是取反加 1)。

      • 按位与运算只会保留这些对应位置上的 1,其他位置会被置为 0。

    举个例子:

    在计算机中,负数是通过 补码(Two's Complement)表示的。负数的补码表示法是将其对应的正数的二进制表示取反后加 1。

             0010 // 2

    • 假设 xorResult = 6,它的二进制是 0110。

    • -xorResult = -6,它的二进制是 1010。

    • 按位与运算:

       0110 // 6 & 1010 // -6  

    • 结果是 2,二进制为 0010。这就是 xorResult 中 最右边的 1 位 所对应的值。

  •         int num1 = 0, num2 = 0;

    第三步:根据该位分组

    xorResult 是两个只出现一次的数字异或的结果,它的二进制表示中至少有一个位是不同的。我们选择 rightmostBit 这个比特位,是因为它是 d 和 e(那两个只出现一次的数字)之间的一个不同的比特位。

    利用该位分组,分为两组:

    • 一组是该位为1的数字。

    • 一组是该位为0的数字。

    我们将整个数组的元素分为两组:一组是该比特位为1的数字,另一组是该比特位为0的数字。我们希望通过这个分组,将两个只出现一次的数字分别归到不同的组里。(每一组只有一个)
        for (int i = 0; i < n; i++) {
            if (arr[i] & rightmostBit) {
                num1 ^= arr[i];
    // 该比特位为 1 的数字
            } else {
                num2 ^= arr[i];
    // 该比特位为 0 的数字
            }
        }

    第四步:对两组进行异或

    对这两组数字分别进行异或操作,最终结果分别就是我们要找的两个数字。

     printf("The two numbers that appear only once are: %d and %d\\n", num1, num2);

    主函数

    int main() {
        int arr[] = {1, 2, 3, 4, 5, 1, 2, 3, 4, 6};
        int n = sizeof(arr) / sizeof(arr[0]);
        findTwoSingleNumbers(arr, n);
        return 0;
    }

    详细函数

    #include <stdio.h>

    void findTwoSingleNumbers(int arr[], int n) {
        int xorResult = 0;
        
        // 第一步:计算所有数字的异或结果
        for (int i = 0; i < n; i++) {
            xorResult ^= arr[i];
        }

        // 第二步:找到一个比特位,使得该位在两个只出现一次的数字之间不同
        int rightmostBit = xorResult & (-xorResult); // 获取最右边的 1 位
        
        int num1 = 0, num2 = 0;
        
        // 第三步:将数组中的数字分为两组,根据该比特位为 1 或 0
        for (int i = 0; i < n; i++) {
            if (arr[i] & rightmostBit) {
                num1 ^= arr[i]; // 该比特位为 1 的数字
            } else {
                num2 ^= arr[i]; // 该比特位为 0 的数字
            }
        }

        // 输出结果
        printf("The two numbers that appear only once are: %d and %d\\n", num1, num2);
    }

    int main() {
        int arr[] = {1, 2, 3, 4, 5, 1, 2, 3, 4, 6};
        int n = sizeof(arr) / sizeof(arr[0]);
        
        findTwoSingleNumbers(arr, n);
        
        return 0;
    }

     

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 找两个单身狗(^的使用应用)(从右向左数第一个为 1 的比特位)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!