添加py,js和cpp,去除多余代码,添加注释

This commit is contained in:
jaredliw 2021-07-23 18:16:12 +08:00
parent ae5cd15279
commit e761b70650

View File

@ -28,62 +28,118 @@
1.通过遍历数组获取所有元素的和以及 HashSet 内元素的和
2.SumSet * 3 - SumNum/ 2即可除以 2 是因为我们减去之后得到的是 2 倍的目标元素
2.SumSet * 3 - SumNum/ 2 即可除以 2 是因为我们减去之后得到的是 2 倍的目标元素
这个题目中需要注意溢出的情况
#### 题目代码
Java Code:
```java
class Solution {
public int singleNumber(int[] nums) {
HashSet<Integer> set = new HashSet<>();
long sumset = 0;
long sumnum = 0;
long setsum = 0;
long numsum = 0;
for (int x : nums) {
//所有元素的和
sumnum += x;
if (set.contains(x)) {
continue;
numsum += x;
if (!set.contains(x)) {
//HashSet元素和
setsum += x;
}
//HashSet元素和
sumset += x;
set.add(x);
}
//返回只出现一次的数
return (int)((3 * sumset - sumnum) / 2);
return (int)((3 * setsum - numsum) / 2);
}
}
```
C++ Code:
```cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_set<int> set;
long setsum = 0;
long numsum = 0;
for (int x : nums) {
//所有元素的和
numsum += x;
if (set.find(x) == set.end()) {
//HashSet内元素的和
setsum += x;
}
set.insert(x);
}
//返回值
return (3 * setsum - numsum) / 2;
}
};
```
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 (3 * setsum - numsum) / 2;
};
```
Python Code:
```python
class Solution:
def singleNumber(self, nums: List[int]) -> int:
return (3 * sum(set(nums)) - sum(nums)) // 2
```
这个题目用 HashMap 和排序查找肯定也是可以的大家可以自己写一下另外我们在第一题中有个利用异或求解的方法但是这个题目是出现三次我们则不能利用直接异或来求解那还有其他方法吗
### 位运算
#### 解析
这个方法主要做法是将我们的数的二进制位每一位相加然后对其每一位的和取余 我们看下面的例子
这个方法主要做法是将我们的数的二进制位每一位相加然后对其每一位的和 3 取余 我们看下面的例子
![只出现一次的数字2](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/只出现一次的数字2.5p4wxbiegxc0.png)
那么我们为什么要这样做呢大家想一下如果其他数都出现 3 只有目标数出现 1 那么每一位的 1 的个数无非有这2种情况 3 的倍数全为出现三次的数 3 的倍数 +1包含出现一次的数这个 3 的倍数 +1 的情况也就是我们的目标数的那一位
那么我们为什么要这样做呢大家想一下如果其他数都出现 3 只有目标数出现 1 那么每一位的 1 的个数无非有这种情况 3 的倍数全为出现三次的数 3 的倍数 +1包含出现一次的数这个 3 的倍数 +1 的情况也就是我们的目标数的那一位
#### 题目代码
Java Code:
```java
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for(int i = 0; i < 32; i++){
int count = 0;
for (int j = 0; j < nums.length; j++) {
//先将数右移并求出最后一位为 1 的个数
if ((nums[j] >> i & 1) == 1) {
for (int num: nums) {
//检查第 i 位是否为 1
if ((num >> i & 1) == 1) {
count++;
}
}
//找到某一位取余为 1 的数并左移为了将这一位循环结束后移至原位
if (count % 3 != 0) {
// 将第 i 位设为 1
res = res | 1 << i;
}
}
@ -92,24 +148,128 @@ class Solution {
}
```
我们来解析一下我们的代码
C++ Code:
> **<<** 左移动运算符运算数的各二进位全部左移若干位 **<<** 右边的数字指定了移动的位数高位丢弃低位补0
```cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for(int i = 0; i < 32; i++){
int count = 0;
for (int num: nums) {
//检查第 i 位是否为 1
if ((num >> i & 1) == 1) {
count++;
}
}
if (count % 3 != 0) {
// 将第 i 位设为 1
res = res | 1 << i;
}
}
return res;
}
};
```
JS Code:
```javascript
var singleNumber = function(nums) {
let res = 0;
for(let i = 0; i < 32; i++){
let count = 0;
for (let num of nums) {
//检查第 i 位是否为 1
if ((num >> i & 1) == 1) {
count++;
}
}
if (count % 3 != 0) {
// 将第 i 位设为 1
res = res | 1 << i;
}
}
return res;
};
```
Python Code:
```python
class Solution:
def singleNumber(self, nums: List[int]) -> int:
res = 0
for i in range(32):
count = 0
for num in nums:
# 检查第 i 位是否为 1
if (num >> i & 1) == 1:
count += 1
if count % 3 != 0:
# 将第 i 位设为 1
res = res | 1 << i
# 这里的做法稍有不同见下方解释
if (res >> 31 & 1) == 1:
res = ~(res ^ 4294967295)
return res
```
我们来解析一下我们的代码
> **<<** 左移运算符运算数的各二进位全部左移若干位 **<<** 右边的数字指定了移动的位数高位丢弃低位补0
>
> **>>** 右移动运算符">>"左边的运算数的各二进位全部右移若干位**>>** 右边的数字指定了移动的位数
> **>>** 右移运算符**>> ** 左边的运算数的各二进位全部右移若干位**>>** 右边的数字指定了移动的位数
另外我们的代码中还包含了 a & 1 a | 1 这有什么作用呢继续看下图
另外我们的代码中还包含了 a & 1 a | 1 这有什么作用呢继续看下图
> **&** 按位与运算符参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
> **&** 按位与运算符参与运算的两个值如果两个相应位都为1,则该位的结果为1否则为0
![只出现一次的数位运算且](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/只出现一次的数位运算且.vq3lcgv0rbk.png)
因为我们 a & 1 1 只有最后一位为 1其余位皆为 0 所以我们发现 a & 1的作用就是判断 a 的最后一位是否为 1 如果 a 的最后一位为 1 a & 1 = 1,否则为 0 所以我们还可以通过这个公式来判断 a 的奇偶性
因为我们 a & 1 1 只有最后一位为 1其余位皆为 0 所以我们发现 **a & 1 的作用就是判断 a 的最后一位是否为 1** 如果 a 的最后一位为 1 a & 1 = 1否则为 0 所以我们还可以通过这个公式来判断 a 的奇偶性
> **|** 按位或运算符只要对应的二个二进位有一个为1时结果位就为1
![或运算](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/或运算.6orep3gsrxc0.png)
这个公式的作用就是将我们移位后的 res 的最后一位 0 变为 1这个 1 也就代表着我们只出现一次元素的某一位
这个公式的作用就是**将我们移位后的 res 的最后一位 0 变为 1**这个 1 也就代表着我们只出现一次元素的某一位
> 贡献者[@jaredliw](https://github.com/jaredliw)
>
> 这里我想解释一下 python 里的这两行
>
> ```python
> if (res >> 31 & 1) == 1:
> res = ~(res ^ 4294967295)
> ```
>
> int 的的符号是由最高位这题用的是32位的值决定1 就是负数0就是正数由于 python int 类型理论上是无限大的这题里的 res 都会被认定为是正数举个例子32位的 -4 是这样的
>
> > 11111111111111111111111111111100 最高位是 1 = -4
>
> python 里的则是这样的
>
> > ...000000000000 11111111111111111111111111111100 前面跟着无限个 0最高位是 0 = 4294967292
>
> 怎么办呢
>
> 我们可以先将 res 的后32位取反 4294967295 异或4294967295 的二进制是 32 1得到
>
> > ...000000000000 00000000000000000000000000000011最高位是0= 3
>
> 之后再用波浪号按位取反得到
>
> > ...111111111111 11111111111111111111111111111100 前面跟着无限个 1最高位是 1= -4
>
> 大家可以自行验证看看`(res >> n & 1) == 1` n 随便填个大于 31 的数字之前是 false之后就变成 true 代表第 33 34 都转成 1
>
> 虽然说这种方法有一种脱裤子放屁的感觉 而且`res -= 2 ** 32` 也能办到但由于涉及到 int 存储的问题我省略了许多大家自行度娘哈我觉得还是有必要知道的