algorithm-base/animation-simulation/求次数问题/只出现一次的数3.md

289 lines
8.3 KiB
Markdown
Raw Normal View History

2021-07-23 15:44:19 +00:00
> 如果阅读时,发现错误,或者动画不可以显示的问题可以添加我微信好友 **[tan45du_one](https://raw.githubusercontent.com/tan45du/tan45du.github.io/master/个人微信.15egrcgqd94w.jpg)** ,备注 github + 题目 + 问题 向我反馈
2021-03-20 08:44:27 +00:00
>
> 感谢支持,该仓库会一直维护,希望对各位有一丢丢帮助。
>
2021-07-23 15:44:19 +00:00
> 另外希望手机阅读的同学可以来我的 <u>[**公众号:袁厨的算法小屋**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u> 两个平台同步,想要和题友一起刷题,互相监督的同学,可以在我的小屋点击<u>[**刷题小队**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u>进入。
2021-03-20 08:44:27 +00:00
#### [260. 只出现一次的数字 III](https://leetcode-cn.com/problems/single-number-iii/)
2021-03-19 07:35:25 +00:00
> 给定一个整数数组 nums其中恰好有两个元素只出现一次其余所有元素均出现两次。 找出只出现一次的那两个元素。
示例 :
> 输入: [1,2,1,3,2,5]
> 输出: [3,5]
2021-07-23 13:18:15 +00:00
这个也很容易理解,算是对第一题的升级,第一题有 1 个出现一次的数,其余出现两次,这个题目中有 2 个出现一次的数,其余数字出现两次。那么这个题目我们怎么做呢?我们看一下能不能利用第一题中的做法解决。
2021-03-19 07:35:25 +00:00
### HashSet
#### 解析
这个做法和我们第一题的做法一致,只要理解了第一题的做法,这个很容易就能写出来,有一点不同的是,第一题的 HashSet 里面最后保留了一个元素,该题保留两个元素。
#### 题目代码
2021-07-23 13:18:15 +00:00
Java Code:
2021-03-19 07:35:25 +00:00
```java
class Solution {
public int[] singleNumber(int[] nums) {
HashSet<Integer> set = new HashSet<Integer>();
for (int x : nums) {
//存在的则移除
if (set.contains(x)) {
set.remove(x);
2021-07-23 13:18:15 +00:00
//不存在则存入
} else {
set.add(x);
2021-03-19 07:35:25 +00:00
}
}
//存到数组里,然后返回
int[] arr = new int[2];
int i = 0;
for (int y : set) {
2021-07-23 15:44:19 +00:00
arr[i++] = y;
2021-03-19 07:35:25 +00:00
}
return arr;
}
}
```
2021-07-23 13:18:15 +00:00
C++ Code:
```cpp
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:
```javascript
2021-07-23 15:44:19 +00:00
var singleNumber = function (nums) {
let set = new Set();
for (let x of nums) {
//存在的则移除
if (set.has(x)) {
set.delete(x);
//不存在则存入
} else {
set.add(x);
2021-07-23 13:18:15 +00:00
}
2021-07-23 15:44:19 +00:00
}
//存到数组里,然后返回
let arr = [];
for (let y of set) {
arr.push(y);
}
return arr;
2021-07-23 13:18:15 +00:00
};
```
Python Code:
```python
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
```
2021-03-19 07:35:25 +00:00
### 位运算
#### 解析
第一题中,我们可以通过异或运算直接求出目标数,但是我们第二题中不能直接用异或,是因为其他数字都出现三次,目标数出现一次。在这个题目中其他数字出现两次,目标数出现一次,但是这次的目标数为两个,我们直接异或运算的话,得到的数则为两个目标数的异或值,那么我们应该怎么做呢?
我们试想一下,如果我们先将元素分成两组,然后每组包含一个目标值,那么异或之后,每组得到一个目标值,那么我们不就将两个目标值求出了吗?
2021-07-23 13:18:15 +00:00
> 例: **a, b, a, b, c, d, e, f, e, f **
>
> 分组后:
2021-03-19 07:35:25 +00:00
>
2021-07-23 15:44:19 +00:00
> A 组a, a, b, b, c 异或得到 c
2021-03-19 07:35:25 +00:00
>
2021-07-23 15:44:19 +00:00
> B 组e, e, f, f, d 异或得到 d
2021-03-19 07:35:25 +00:00
原理懂了,那么我们应该依据什么规则对其进行分类呢?
2021-07-23 15:44:19 +00:00
cd 两个不同的数,那么二进制上必定有一位是不同的,那么我们就可以根据这一位(分组位)来将 cd 分到两个组中,数组中的其他元素,要么在 A 组中,要么在 B 组中。
2021-03-19 07:35:25 +00:00
我们应该怎么得到分组位呢?
2021-07-23 13:18:15 +00:00
我们让 cd 异或即可,异或运算就是对应位不同时得 1异或之后值为 1 的其中一位则为我们分组。
2021-03-19 07:35:25 +00:00
2021-07-23 13:18:15 +00:00
例 001 ⊕ 100 = 101我们可以用最右边的 1 或最左边的 1 做为分组位,数组元素中,若我们将最右边的 1 作为我们的分组位,最后一位为 0 的则进入 A 组,为 1 的进入 B 组。
2021-03-19 07:35:25 +00:00
那么我们应该怎么借助分组位进行分组呢?
2021-07-23 15:44:19 +00:00
我们处理 c , d 的异或值,可以仅保留异或值的分组位,其余位变为 0 ,例如 101 变成 001 或 100。
2021-03-19 07:35:25 +00:00
2021-07-23 13:18:15 +00:00
为什么要这么做呢?在第二题提到,我们可以根据 a & 1 来判断 a 的最后一位为 0 还是为 1所以我们将 101 变成 001 之后,然后数组内的元素 x & 001 即可对 x 进行分组 。同样也可以 x & 100 进行分组。
2021-03-19 07:35:25 +00:00
2021-07-23 13:18:15 +00:00
那么我们如何才能仅保留分组位,其余位变为 0 呢?例 101 变为 001。
2021-03-19 07:35:25 +00:00
2021-07-23 13:18:15 +00:00
我们可以利用 x & (-x) 来保留最右边的 1。
2021-03-19 07:35:25 +00:00
![分组位](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/分组位.25gbi25kv7c0.png)
#### 题目代码:
2021-07-23 13:18:15 +00:00
Java Code:
2021-03-19 07:35:25 +00:00
```java
class Solution {
public int[] singleNumber(int[] nums) {
2021-07-23 13:18:15 +00:00
//小心溢出
long temp = 0;
2021-03-19 07:35:25 +00:00
//求出异或值
for (int x : nums) {
temp ^= x;
}
2021-07-23 13:18:15 +00:00
System.out.println(temp);
System.out.println(-temp);
2021-03-19 07:35:25 +00:00
//保留最右边的一个 1
2021-07-23 13:18:15 +00:00
long group = temp & (-temp);
2021-03-19 07:35:25 +00:00
int[] arr = new int[2];
2021-07-23 15:44:19 +00:00
for (int y : nums) {
2021-07-23 13:18:15 +00:00
//分组位为 0 的组,组内异或
2021-03-19 07:35:25 +00:00
if ((group & y) == 0) {
arr[0] ^= y;
2021-07-23 13:18:15 +00:00
//分组位为 1 的组,组内异或
2021-03-19 07:35:25 +00:00
} else {
arr[1] ^= y;
}
}
return arr;
}
}
```
2021-07-23 13:18:15 +00:00
C++ Code:
```cpp
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);
2021-07-23 15:44:19 +00:00
for (int y : nums) {
2021-07-23 13:18:15 +00:00
//分组位为 0 的组,组内异或
if ((group & y) == 0) {
arr[0] ^= y;
//分组位为 1 的组,组内异或
} else {
arr[1] ^= y;
}
}
return arr;
}
};
```
JS Code:
```javascript
2021-07-23 15:44:19 +00:00
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;
2021-07-23 13:18:15 +00:00
}
2021-07-23 15:44:19 +00:00
}
return arr;
2021-07-23 13:18:15 +00:00
};
```
Python Code:
```python
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
```
2021-07-27 18:26:32 +00:00
Go Code:
```go
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
}
```