3、只出现一次的数3

厨子大约 5 分钟数据结构算法算法基地面试刷题

题目描述

只出现一次的数字 IIIopen in new window

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

示例 :

输入: [1,2,1,3,2,5] 输出: [3,5]

这个也很容易理解,算是对第一题的升级,第一题有 1 个出现一次的数,其余出现两次,这个题目中有 2 个出现一次的数,其余数字出现两次。那么这个题目我们怎么做呢?我们看一下能不能利用第一题中的做法解决。

HashSet

解析

这个做法和我们第一题的做法一致,只要理解了第一题的做法,这个很容易就能写出来,有一点不同的是,第一题的 HashSet 里面最后保留了一个元素,该题保留两个元素。

题目代码

Java Code:

class Solution {
    public int[] singleNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<Integer>();
        for (int x : nums) {
            //存在的则移除
            if (set.contains(x)) {
                set.remove(x);
            //不存在则存入
            } else {
                set.add(x);
            }
        }
        //存到数组里,然后返回
        int[] arr = new int[2];
        int i = 0;
        for (int y : set) {
           arr[i++] = y;
        }
        return arr;
    }
}

C++ Code:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        unordered_set<int> set;
        for (int x : nums) {
            //存在的则移除
            if (set.find(x) == set.end()) {
                set.insert(x);
            //不存在则存入
            } else {
                set.erase(x);
            }
        }
        //存到数组里,然后返回
        vector<int> arr;
        for (int y : set) {
            arr.push_back(y);
        }
        return arr;
    }
};

JS Code:

var singleNumber = function (nums) {
  let set = new Set();
  for (let x of nums) {
    //存在的则移除
    if (set.has(x)) {
      set.delete(x);
      //不存在则存入
    } else {
      set.add(x);
    }
  }
  //存到数组里,然后返回
  let arr = [];
  for (let y of set) {
    arr.push(y);
  }
  return arr;
};

Python Code:

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        set_ = set()
        for x in nums:
            # 存在的则移除
            if x in set_:
                set_.remove(x)
            # 不存在则存入
            else:
                set_.add(x)
        # 存到数组里,然后返回
        arr = []
        for y in set_:
            arr.append(y)
        return arr

位运算

解析

第一题中,我们可以通过异或运算直接求出目标数,但是我们第二题中不能直接用异或,是因为其他数字都出现三次,目标数出现一次。在这个题目中其他数字出现两次,目标数出现一次,但是这次的目标数为两个,我们直接异或运算的话,得到的数则为两个目标数的异或值,那么我们应该怎么做呢?

我们试想一下,如果我们先将元素分成两组,然后每组包含一个目标值,那么异或之后,每组得到一个目标值,那么我们不就将两个目标值求出了吗?

例: **a, b, a, b, c, d, e, f, e, f **

分组后:

A 组:a, a, b, b, c 异或得到 c

B 组:e, e, f, f, d 异或得到 d

原理懂了,那么我们应该依据什么规则对其进行分类呢?

c,d 两个不同的数,那么二进制上必定有一位是不同的,那么我们就可以根据这一位(分组位)来将 c,d 分到两个组中,数组中的其他元素,要么在 A 组中,要么在 B 组中。

我们应该怎么得到分组位呢?

我们让 c,d 异或即可,异或运算就是对应位不同时得 1,异或之后值为 1 的其中一位则为我们分组。

例 001 ⊕ 100 = 101,我们可以用最右边的 1 或最左边的 1 做为分组位,数组元素中,若我们将最右边的 1 作为我们的分组位,最后一位为 0 的则进入 A 组,为 1 的进入 B 组。

那么我们应该怎么借助分组位进行分组呢?

我们处理 c , d 的异或值,可以仅保留异或值的分组位,其余位变为 0 ,例如 101 变成 001 或 100。

为什么要这么做呢?在第二题提到,我们可以根据 a & 1 来判断 a 的最后一位为 0 还是为 1,所以我们将 101 变成 001 之后,然后数组内的元素 x & 001 即可对 x 进行分组 。同样也可以 x & 100 进行分组。

那么我们如何才能仅保留分组位,其余位变为 0 呢?例 101 变为 001。

我们可以利用 x & (-x) 来保留最右边的 1。

分组位
分组位

题目代码

Java Code:

class Solution {
    public int[] singleNumber(int[] nums) {
        //小心溢出
        long temp = 0;
        //求出异或值
        for (int x : nums) {
            temp ^= x;
        }
        System.out.println(temp);
        System.out.println(-temp);
        //保留最右边的一个 1
        long group = temp & (-temp);
        int[] arr = new int[2];
        for (int y : nums) {
            //分组位为 0 的组,组内异或
            if ((group & y) == 0) {
                arr[0] ^= y;
            //分组位为 1 的组,组内异或
            } else {
                arr[1] ^= y;
            }
        }
        return arr;
    }
}

C++ Code:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        //小心 -temp 溢出
        long temp = 0;
        //求出异或值
        for (int x : nums) {
            temp ^= x;
        }
        //保留最右边的一个 1
        int group = temp & (-temp);
        vector<int> arr(2, 0);
        for (int y : nums) {
            //分组位为 0 的组,组内异或
            if ((group & y) == 0) {
                arr[0] ^= y;
            //分组位为 1 的组,组内异或
            } else {
                arr[1] ^= y;
            }
        }
        return arr;
    }
};

JS Code:

var singleNumber = function (nums) {
  let temp = 0;
  //求出异或值
  for (let x of nums) {
    temp ^= x;
  }
  //保留最右边的一个 1
  let group = temp & -temp;
  let arr = [0, 0];
  for (let y of nums) {
    //分组位为 0 的组,组内异或
    if ((group & y) == 0) {
      arr[0] ^= y;
      //分组位为 1 的组,组内异或
    } else {
      arr[1] ^= y;
    }
  }
  return arr;
};

Python Code:

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        temp = 0
        # 求出异或值
        for x in nums:
            temp ^= x
        # 保留最右边的一个 1
        group = temp & (-temp)
        arr = [0, 0]
        for y in nums:
            # 分组位为 0 的组,组内异或
            if (group & y) == 0:
                arr[0] ^= y
            # 分组位为 1 的组,组内异或
            else:
                arr[1] ^= y
        return arr

Go Code:

func singleNumber(nums []int) []int {
    temp := 0
    for _, x := range nums {
        temp ^= x
    }
    // 保留最后那个1,为了区分两个数
    group := temp & (^temp + 1)

    res := make([]int, 2)
    for _, x := range nums {
        if group & x == 0{
            res[0] ^= x
        } else {
            res[1] ^= x
        }
    }
    return res
}