algorithm-base/animation-simulation/求次数问题/只出现一次的数.md
2021-07-22 23:12:26 +08:00

582 lines
15 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>进入。
#### [136. 只出现一次的数字](https://leetcode-cn.com/problems/single-number/)
> 给定一个非空整数数组除了某个元素只出现一次以外其余每个元素均出现两次找出那个只出现了一次的元素
示例 1:
> 输入: [2,2,1]
> 输出: 1
示例 2:
> 输入: [4,1,2,1,2]
> 输出: 4
这个题目非常容易理解就是让我们找出那个只出现一次的数字那么下面我们来看一下这几种解题方法吧~
### HashMap
#### 解析
HashMap 的这个方法是很容易实现的题目要求不是让我们求次数嘛那我们直接遍历数组将每个数字和其出现的次数存到哈希表里就可以了然后我们再从哈希表里找出出现一次的那个数返回即可
![哈希表解法](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/哈希表解法.1kefww8xsig0.png)
#### 题目代码
Java Code:
```java
class Solution {
public int singleNumber(int[] nums) {
//特殊情况
if (nums.length == 1) {
return nums[0];
}
//HashMap
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
//将其存入哈希表中含义为若该元素不存在则存入表中并计数为1若已经存在获取次数并加1
for (int x : nums) {
map.put(x , map.getOrDefault(x,0) + 1);
}
//遍历出出现次数为1的情况
for (int y : map.keySet()) {
if(map.get(y) == 1){
return y;
}
}
}
}
```
C++ Code:
```cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
//特殊情况
if (nums.size() == 1) {
return nums[0];
}
//HashMap
unordered_map<int,int> map;
//将其存入哈希表中含义为若该元素不存在则存入表中并计数为1若已经存在获取次数并加1
for (int x : nums) {
if (map.find(x) == map.end()) {
map.insert({x, 0});
}
map[x] += 1;
}
//遍历出出现次数为1的情况
for (int y : nums) {
if(map.at(y) == 1){
return y;
}
}
return -1;
}
};
```
JS Code:
```javascript
var singleNumber = function(nums) {
//特殊情况
if (nums.length === 1) {
return nums[0];
}
//HashMap
map = {};
//将其存入哈希表中含义为若该元素不存在则存入表中并计数为1若已经存在获取次数并加1
for (let x of nums) {
if (!(x in map)) {
map[x] = 0;
}
map[x] += 1;
}
//遍历出出现次数为1的情况
for (let key in map) {
if (map[key] === 1) {
return key;
}
}
};
```
Python Code:
```python
class Solution:
def singleNumber(self, nums: List[int]) -> int:
# 特殊情况
if len(nums) == 1:
return nums[0]
# HashMap
map_ = {}
# 将其存入哈希表中含义为若该元素不存在则存入表中并计数为1若已经存在获取次数并加1
for x in nums:
map_.setdefault(x, 0)
map_[x] += 1
# 遍历出出现次数为1的情况
for y, count in map_.items():
if count == 1:
return y
```
### 排序搜索法
#### 解析
这个方法也是特别容易想到的我们首先对数组进行排序然后遍历数组因为数组中其他数字都出现两次只有目标值出现一次所以则让我们的指针每次跳两步当发现当前值和前一位不一样的情况时返回前一位即可当然我们需要考虑这种情况当我们的目标值出现在数组最后一位的情况所以当数组遍历结束后没有返回值则我们需要返回数组最后一位下面我们看一下动图解析
![排序](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/排序.6sp72k3iaqw0.gif)
#### 题目代码
Java Code:
```java
class Solution {
public int singleNumber(int[] nums) {
if (nums.length == 1){
return nums[0];
}
//排序
Arrays.sort(nums);
for (int i = 1; i < nums.length-1; i+=2){
if (nums[i] != nums[i-1]){
return nums[i-1];
}
}
return nums[nums.length-1];
}
}
```
C++ Code:
```cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
//排序
sort(nums.begin(), nums.end());
for (int i = 1; i < nums.size() - 1; i += 2){
if (nums[i] != nums[i - 1]){
return nums[i - 1];
}
}
return nums[nums.size() - 1];
}
};
```
JS Code:
```javascript
var singleNumber = function(nums) {
// 排序
nums.sort();
for (let i = 1; i < nums.length - 1; i += 2) {
if (nums[i] != nums[i - 1]) {
return nums[i - 1];
}
}
return nums[nums.length - 1];
};
```
Python Code:
```python
class Solution:
def singleNumber(self, nums: List[int]) -> int:
# 排序
nums.sort()
for i in range(1, len(nums), 2):
if nums[i] != nums[i - 1]:
return nums[i - 1]
return nums[len(nums) - 1]
```
### HashSet
#### 解析
这个方法也是比较容易实现的我们利用 HashSet 来完成HashSet 在我们刷题时出现频率是特别高的它是基于 HashMap 来实现的是一个不允许有重复元素的集合那么在这个题解中它起到什么作用呢解题思路如下我们依次遍历元素并与 HashSet 内的元素进行比较如果 HashSet 内没有该元素说明该元素第一次出现则存入若是 HashSet 已经存在该元素(第二次出现)则将其从 HashSet 中去除并继续遍历下一个元素最后 HashSet 内剩下的则为我们的目标数思路和我们之前说过的括号匹配问题类似我们一起来看一下动图解析吧
![HashSet](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/HashSet.4b6dcxwj07c0.gif)
#### 题目代码
Java Code:
```java
class Solution {
public int singleNumber(int[] nums) {
HashSet<Integer> set = new HashSet<>();
//循环遍历
for (int x : nums){
//已经存在,则去除
if(set.contains(x)){
set.remove(x);
//否则存入
} else {
set.add(x);
}
}
//返回仅剩的一个元素
return set.iterator().next();
}
}
```
C++ Code:
```cpp
class Solution {
public:
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);
}
}
//返回仅剩的一个元素
return *set.begin();
}
};
```
JS Code:
```javascript
var singleNumber = function(nums) {
let set = new Set();
//循环遍历
for (let x of nums) {
//已经存在,则去除
if (set.has(x)) {
set.delete(x);
//否则存入
} else {
set.add(x);
}
}
return set.values().next().value;
};
```
Python Code:
```python
class Solution:
def singleNumber(self, nums: List[int]) -> int:
set_ = set()
# 循环遍历
for x in nums:
# 已经存在则去除
if x in set_:
set_.remove(x)
# 否则存入
else:
set_.add(x)
# 返回仅剩的一个元素
return set_.pop()
```
###
#### 解析
该方法也很容易想到我们首先将其排序然后遍历数组如果栈为空则将当前元素压入栈如果栈不为空若当前元素和栈顶元素相同则出栈继续遍历下一元素如果当前元素和栈顶元素不同的话则说明栈顶元素是只出现一次的元素我们将其返回即可这个题目也可以使用队列做思路一致我们就不在这里说明啦下面我们看下动图解析
![](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/栈.6mzstgebww00.gif)
#### 题目代码
Java Code:
```java
class Solution {
public int singleNumber(int[] nums) {
Arrays.sort(nums);
Stack<Integer> stack = new Stack<>();
for (int x : nums){
if (stack.isEmpty()) {
stack.push(x);
continue;
}
//不同时直接跳出
if (stack.peek() != x) {
break;
}
//相同时出栈
stack.pop();
}
return stack.peek();
}
}
```
C++ Code:
```cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());
stack<int> stack;
for (int x: nums) {
if (stack.empty()) {
stack.push(x);
continue;
}
//不同时直接跳出
if (stack.top() != x) {
break;
}
//相同时出栈
stack.pop();
}
return stack.top();
}
};
```
JS Code:
```javascript
var singleNumber = function(nums) {
nums.sort();
let stack = [];
for (let x of nums) {
if (!stack.length) {
stack.push(x);
continue;
}
//不同时直接跳出
if (stack[0] !== x) {
break;
}
//相同时出栈
stack.pop();
}
return stack[0];
};
```
Python Code:
```python
from collections import deque
class Solution:
def singleNumber(self, nums: List[int]) -> int:
if len(nums) == 1:
return nums[0]
nums.sort()
stack = deque()
for x in nums:
if not stack:
stack.append(x)
continue
# 不同时直接跳出
if stack[-1] != x:
break
# 相同时出栈
stack.pop()
return stack[-1]
```
### 求和法
#### 解析
这个方法也比较简单也是借助咱们的 HashSet 具体思路如下我们通过 HashSet 保存数组内的元素然后进行求和setsum那么得到的这个和则为去除掉重复元素的和我们也可以得到所有元素和numsum因为我们其他元素都出现两次仅有一个元素出现一次那我们通过 setsum * 2 - numsum 得到的元素则为出现一次的数
![求和解法](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/求和解法.2tds49a3vzq0.png)
上面我们的 SetSum * 2 - NumSum = z 也就是我们所求的值 是不是感觉很简单呀
#### 题目代码
Java Code:
```java
class Solution {
public int singleNumber(int[] nums) {
HashSet<Integer> set = new HashSet<>();
int setsum = 0;
int numsum = 0;
for (int x : nums) {
//所有元素的和
numsum += x;
if (!set.contains(x)) {
//HashSet内元素的和
setsum += x;
}
set.add(x);
}
//返回值
return setsum * 2 - numsum;
}
}
```
C++ Code:
```cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_set<int> set;
int setsum = 0;
int numsum = 0;
for (int x : nums) {
//所有元素的和
numsum += x;
if (set.find(x) == set.end()) {
//HashSet内元素的和
setsum += x;
}
set.insert(x);
}
//返回值
return setsum * 2 - numsum;
}
};
```
JS Code:
```javascript
var singleNumber = function(nums) {
let set = new Set();
let setsum = 0;
let numsum = 0;
for (let x of nums) {
//所有元素的和
numsum += x;
if (!set.has(x)) {
setsum += x;
}
//HashSet内元素的和
set.add(x);
}
//返回值
return 2 * setsum - numsum;
};
```
Python Code:
```python
class Solution:
def singleNumber(self, nums: List[int]) -> int:
return 2 * sum(set(nums)) - sum(nums)
```
### 位运算
#### 解析
这个方法主要是借助咱们的位运算符 ^ 按位异或我们先来了解一下这个位运算符
> 按位异或XOR运算符^是双目运算符 其功能是参与运算的两数各对应的二进位相异或当两对应的二进位相异时结果为1相同时为0
> 任何数和0异或仍为本身a0 = a
> 任何数和本身异或为0aa = 0
> 异或运算满足交换律和结合律aba = (aa)b = 0b = b
![异或运算](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/异或运算.1myeo11xgqo0.png)
我们通过上面的例子了解了异或运算对应位相异时得 1相同时得 0那么某个数跟本身异或时因为对应位都相同所以结果为 0 然后异或又满足交换律和结合律
![image-20201129120648802](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/image-20201129120648802.2ajgng6zzd7o.png)
#### 题目代码
Java Code:
```java
class Solution {
public int singleNumber(int[] nums) {
int num = 0;
//异或
for (int x : nums) {
num ^= x;
}
return num;
}
}
```
C++ Code:
```cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int num = 0;
//异或
for (vector<int>::iterator x = nums.begin(); x != nums.end(); x++) {
num ^= *x;
}
return num;
}
};
```
JS Code:
```javascript
var singleNumber = function(nums) {
//异或
return nums.reduce((num, x) => num ^= x);
};
```
Python Code:
```python
from functools import reduce
class Solution:
def singleNumber(self, nums: List[int]) -> int:
# 异或
return reduce(lambda num, x: num ^ x, nums, 0)
```
本题一共介绍了6种解题方法肯定还有别的方法欢迎大家讨论大家可以在做题的时候一题多解这样能大大提高自己解题能力下面我们来看一下这些方法如何应用到其他题目上