返回

位运算 - 巧妙求解 LeetCode 260 - 只出现一次的数字 3

前端

数组中可能包含重复的数字,但我们保证其中一定存在两个只出现一次的数字。你的任务是找出这两个独一无二的数字。

/**
 * 给定一个包含重复数字的数组 nums ,返回其中唯一出现的两个数字。
 *
 * 示例 1:
 * 输入:nums = [1,2,1,3,2,5]
 * 输出:[3,5]
 * 解释:[5, 3] 也是有效的答案。
 *
 * 示例 2:
 * 输入:nums = [-1,0]
 * 输出:[-1,0]
 *
 * 示例 3:
 * 输入:nums = [0,1,0]
 * 输出:[1]
 *
 * 示例 4:
 * 输入:nums = [1,1,1,2,2,2,3,3,3,4,4,4,5,5,5]
 * 输出:[2, 4]
 */
const singleNumber = (nums) => {
  // 第一步:计算所有数字的异或值
  let xor = 0;
  for (const num of nums) {
    xor ^= num;
  }

  // 第二步:找到异或值二进制表示中第一个为 1 的位置
  let i = 0;
  while (xor & (1 << i) === 0) {
    i++;
  }

  // 第三步:将数组分为两部分,一部分是异或值为 1 的数字,另一部分是异或值为 0 的数字
  const group1 = [];
  const group2 = [];
  for (const num of nums) {
    if (num & (1 << i)) {
      group1.push(num);
    } else {
      group2.push(num);
    }
  }

  // 第四步:在两部分中分别找出只出现一次的数字
  const result = [];
  for (const num of group1) {
    if (group1.filter((n) => n === num).length === 1) {
      result.push(num);
    }
  }
  for (const num of group2) {
    if (group2.filter((n) => n === num).length === 1) {
      result.push(num);
    }
  }

  return result;
};

在这个算法中,我们使用了位运算来巧妙地找出两个只出现一次的数字。具体步骤如下:

  1. 计算所有数字的异或值 :异或运算符(^)可以将两个数字的二进制位逐位异或,相同则为 0,不同则为 1。因此,将数组中所有数字的异或值计算出来,就可以得到一个只包含两个只出现一次的数字的异或值。

  2. 找到异或值二进制表示中第一个为 1 的位置 :异或值二进制表示中第一个为 1 的位置,就是两个只出现一次的数字在二进制表示中不同的地方。

  3. 将数组分为两部分 :根据异或值二进制表示中第一个为 1 的位置,将数组分为两部分,一部分是异或值为 1 的数字,另一部分是异或值为 0 的数字。

  4. 在两部分中分别找出只出现一次的数字 :在两部分中分别找出只出现一次的数字。由于两部分的数字都只出现一次,因此我们可以通过遍历数组,并统计每个数字出现的次数,来找出只出现一次的数字。

使用这种方法,我们可以在 O(n) 的时间复杂度内找到两个只出现一次的数字。这种方法简洁高效,并且很容易理解和实现。

我希望这篇文章对您有所帮助。如果您有任何问题或建议,请随时告诉我。