文章目录
碎碎念
好!自此已经刷完hot100的所有简单题!只是还欠了好多篇题解还没写,一点一点跟上吧!今天还学了一点简单的响应式布局,还差一个flex布局就真的算小小小小入门前端啦,项目也有一点点在拉起来了,今天很好很好!
一、题目

二、思路和题解
1.思路
这题首先想到的是用集合,遍历整个整个nums,出现第一次就加入集合,出现第二次再从集合中删除,当遍历完整个数组,就只剩下只出现一次的数啦。但是!思路很美好,苯人技术还是有比较大的进步空间的...我不是很会用集合...
所以还是看了一下题解学到一个思路,是用异或来做。
我们知道异或是 "同0异1" ,基本的运算规则如下:
0 ⊕ 0 = 0 0 ⊕ 1 = 1 1 ⊕ 0 = 1 1 ⊕ 1 = 0 0 \oplus 0=0\\ 0 \oplus 1=1\\ 1 \oplus 0=1\\ 1 \oplus 1=0 0⊕0=00⊕1=11⊕0=11⊕1=0
所以当我们初始化一个res=0,之后遍历整个数组,对每个数都做异或运算,这样只要出现两次的数就会自己和自己异或"消除掉"了,最后剩下的数就相当于与0异或还是它自己,也就是我们要找的只出现一次的数了。
2.代码(位运算,异或)
cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res=0;
for (auto v:nums){
res^=v;
}
return res;
}
};
三、其他解法
这里当然就得补充一下咱们刚刚提到的集合的做法了(在学了在学了!)
1.集合增删法
思路就不赘述了,直接上代码。
cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
// acm的话记得包含<unordered_set>头文件
unordered_set<int> s; // 定义无序集合,查询/增删效率为O(1)
for (int num : nums) {
if (s.count(num)) { // 集合中已有该数字(出现第二次)
s.erase(num); // 从集合删除
} else { // 集合中无该数字(出现第一次)
s.insert(num); // 加入集合
}
}
// 最终集合中只有一个元素,返回该元素(*s.begin()取第一个元素)
return *s.begin();
}
};
2.哈希表统计次数
先用哈希表来存储「数字 → 出现次数」,接着遍历数组统计每个数字的出现次数,最后再遍历哈希表,找到出现次数是1次的数字,就是我们的答案啦。
cpp
#include <vector>
#include <unordered_map> // 哈希表头文件
using namespace std;
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_map<int, int> count_map; // key=数字,value=出现次数
// 统计每个数字的出现次数
for (int num : nums) {
count_map[num]++; // 不存在的key会自动初始化为0,再+1
/*也可以写成下面这样,更好理解一点:
看一下num在不在这个哈希表中,如果不在哈希表中,则查找到的是哈希表的尾后迭代器,那就需要新增。
if (count_map.find(num) != count_map.end()) {
count_map[num] += 1;
} else {
count_map[num] = 1;
}*/
}
// 遍历哈希表找次数=1的数字
for (auto& pair : count_map) {
if (pair.second == 1) { // pair.second 是次数
return pair.first; // pair.first 是数字
}
}
return -1; // 题目保证有解,仅防止编译报错
}
};
3.集合求和差值
这个稍微有点技巧性(我觉得),这里我们用到的是这样一个公式:
唯一数 = 2 × ( 集合所有元素和 ) − ( 数组所有元素和 ) 唯一数=2×(集合所有元素和)−(数组所有元素和) 唯一数=2×(集合所有元素和)−(数组所有元素和)
- 集合存储所有唯一元素,因此「集合和 ×2」等价于「所有元素都出现 2 次的总和」;
- 数组总和 = 「唯一数 ×1」 + 「其余数 ×2」;
- 两者的差值就是唯一数(其余数的 2 倍被抵消,只剩唯一数)。
cpp
#include <vector>
#include <unordered_set>
#include <numeric> // 求和函数 accumulate 的头文件
using namespace std;
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_set<int> s(nums.begin(), nums.end()); // 集合存储所有唯一元素
// 手动遍历求和
long long sum_set = 0;
for (int num : s) {
sum_set += num;
}
// 也可以用STL的accumulate函数,但acm要记得包含<numeric>头文件
// long long sum_set = accumulate(s.begin(), s.end(), 0LL);//0LL表示以 long long 类型初始化累加值,否则 accumulate 会默认用 int 累加,可能溢出
// 计算数组所有元素的和
long long sum_nums = accumulate(nums.begin(), nums.end(), 0LL);
return 2 * sum_set - sum_nums;
}
};
四、错误回顾
不会用集合和哈希表。