algorithm-base/animation-simulation/求次数问题/只出现一次的数3.md
2021-03-20 16:44:27 +08:00

114 lines
4.9 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

> 如果阅读时发现错误或者动画不可以显示的问题可以添加我微信好友 **[tan45du_one](https://raw.githubusercontent.com/tan45du/tan45du.github.io/master/个人微信.15egrcgqd94w.jpg)** ,备注 github + 题目 + 问题 向我反馈
>
> 感谢支持该仓库会一直维护希望对各位有一丢丢帮助
>
> 另外希望手机阅读的同学可以来我的 <u>[**公众号袁厨的算法小屋**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u> 两个平台同步,想要和题友一起刷题,互相监督的同学,可以在我的小屋点击<u>[**刷题小队**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u>进入。
#### [260. 只出现一次的数字 III](https://leetcode-cn.com/problems/single-number-iii/)
> 给定一个整数数组 nums其中恰好有两个元素只出现一次其余所有元素均出现两次 找出只出现一次的那两个元素
示例 :
> 输入: [1,2,1,3,2,5]
> 输出: [3,5]
这个也很容易理解算是对第一题的升级第一题有 1 个出现 1次的数其余出现两次这个题目中有 2 个出现 1次的数其余数字出现两次那么这个题目我们怎么做呢我们看一下能不能利用第一题中的做法解决
### HashSet
#### 解析
这个做法和我们第一题的做法一致只要理解了第一题的做法这个很容易就能写出来有一点不同的是第一题的 HashSet 里面最后保留了一个元素该题保留两个元素
#### 题目代码
```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);
continue;
}
//不存在存入
set.add(x);
}
//存到数组里,然后返回
int[] arr = new int[2];
int i = 0;
for (int y : set) {
arr[i++] = 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
![分组位](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/分组位.25gbi25kv7c0.png)
#### 题目代码
```java
class Solution {
public int[] singleNumber(int[] nums) {
int temp = 0;
//求出异或值
for (int x : nums) {
temp ^= x;
}
//保留最右边的一个 1
int group = temp & (-temp);
System.out.println(group);
int[] arr = new int[2];
for (int y : nums) {
//分组位为0的组组内异或
if ((group & y) == 0) {
arr[0] ^= y;
//分组位为 1 的组,组内异或
} else {
arr[1] ^= y;
}
}
return arr;
}
}
```