文章目录
- 刷题指南
- 总结(***)
- 哈希表的常用方法:统计出现的次数
- [242. 有效的字母异位词](https://leetcode-cn.com/problems/valid-anagram/)
- [349. 两个数组的交集](https://leetcode-cn.com/problems/intersection-of-two-arrays/)
- [202. 快乐数](https://leetcode-cn.com/problems/happy-number/)
- [1. 两数之和](https://leetcode-cn.com/problems/two-sum/)
- [454. 四数相加 II](https://leetcode-cn.com/problems/4sum-ii/)
- [383. 赎金信](https://leetcode-cn.com/problems/ransom-note/)
- [15. 三数之和(排序 + 双指针)](https://leetcode-cn.com/problems/3sum/)
- [18. 四数之和(排序 + 双指针,同三数之和)](https://leetcode-cn.com/problems/4sum/)
刷题指南
总结(***)
1.循环中降时间复杂度的有:排序 + 双指针,哈希表
2.当出现多个变量时,例如a[i] + a[j] == 0,可以固定j,即找0 ~ j - 1中是否存在 -a[j]这个数,可以边求边统计
3.哈希表:统计出现的次数!
4.双指针:满足单调性时可以考虑双指针
哈希表的常用方法:统计出现的次数
哈希法也是牺牲了空间换取了时间
242. 有效的字母异位词
分析:
用哈希表统计每个字符出现的次数,时间复杂度O(n)
。第一次做的时候是直接排序,时间复杂度O(nlogn)
。
当然这题映射范围比较小,可以拿数组做哈希表。进阶的问题范围大,就不能用数组了吧。
「而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。」
class Solution {
public:
bool isAnagram(string s, string t) {
// 时间复杂度O(n)
// 空间复杂度O(26)
if(s.size() != t.size()) return false;
unordered_map<char,int> cnt; // 哈希表
for(auto c : s) cnt[c] ++;
for(auto c : t){
cnt[c] -- ;
}
for(auto x : cnt){
if(x.second != 0) return false;
}
return true;
}
};
349. 两个数组的交集
解法1:使用unordered_set
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
// 时间复杂度O(n)
unordered_set<int> num, res;
for(auto c : nums1) num.insert(c); // 把nums1的元素插到unordered_set中
for(auto c : nums2)
if(num.count(c)) res.insert(c);
vector<int> ans(res.begin(), res.end()); // 赋值
return ans;
}
};
解法2:使用unordered_map
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
// 哈希表
unordered_map<int,int> hash;
for(auto c : nums1) hash[c] ++ ;
vector<int> res;
for(auto c : nums2)
{
if(hash[c] > 0 ) res.push_back(c), hash[c] = 0 ;
}
return res;
}
};
202. 快乐数
分析:
题目中说了会 「无限循环」,那么也就是说「求和的过程中,sum会重复出现
,这对解题很重要!」
class Solution {
public:
bool isHappy(int n) {
unordered_map<int,int> cnt;
while(n != 1)
{
cnt[n] ++;
dfs(n); // 引用改变n的值
if(cnt[n] > 1) return false; // 只要出现次数大于1,说明循环了
}
return true;
}
void dfs(int &n)
{
int res = 0;
while(n > 0){
int t = n % 10;
res += t * t;
n /= 10;
}
n = res;
}
};
1. 两数之和
分析:
一次性统计不太好,边求边插入比较好
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> hash;
// 边求边插入
for(int i = 0;i < nums.size();i ++ ){
if(hash.count(target - nums[i])) return {hash[target - nums[i]], i};
hash[nums[i]] = i;
}
return {-1, -1};
}
};
454. 四数相加 II
分析:
观察到N = 500
,可知最多循环两个数组。常见的处理手法!
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int,int> hash; // 统计 a + b的值出现的次数
for(int i = 0;i < A.size();i ++ )
for(int j = 0;j < B.size();j ++)
hash[A[i] + B[j]] ++;
int res = 0;
for(int i = 0;i < C.size();i ++ )
for(int j = 0;j < D.size();j ++)
{
int s = C[i] + D[j];
if(hash.count(0 - s)) res += hash[0 - s]; // 这里的处理类似 两数之和
}
return res;
}
};
383. 赎金信
class Solution {
public:
bool canConstruct(string r, string m) {
// 哈希计数
unordered_map<char,int> cnt;
for(auto c : m) cnt[c] ++;
for(auto c : r){
cnt[c] -- ;
if(cnt[c] < 0) return false; // 字符不够了
}
return true;
}
};
15. 三数之和(排序 + 双指针)
分析:排序 + 双指针
哈希表需要去重太难写了,改用排序 + 双指针,事件复杂度O(n ^ 2)
找 a[i] + a[l] + a[r] == 0 ,可以枚举a[i],由于之前排序了,必然满足单调性,随着i移动,l,r都会单调移动,所以可以用双指针。
去重的常用操作
:本题的难点也在于去重!
while(l < r && nums[l] == nums[l - 1]) l ++ ;
while(l < r && nums[r] == nums[r + 1]) r -- ;
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
int n = nums.size();
if(n < 3) return res;
sort(nums.begin(), nums.end());
// a[i] + a[l] + a[r] == 0 , 固定a[i]
for(int i = 0;i < n;i ++ )
{
if(nums[i] > 0) return res; // 直接返回了
if(i != 0 && nums[i] == nums[i - 1]) continue; // 去重!关键!
int l = i + 1, r = n - 1;
while(l < r)
{
if(nums[l] + nums[i] + nums[r] == 0) {
res.push_back({nums[i], nums[l], nums[r]});
l ++ , r -- ;
// 去重的常见操作
while(l < r && nums[l] == nums[l - 1]) l ++ ;
while(l < r && nums[r] == nums[r + 1]) r -- ;
}else if(nums[l] + nums[i] + nums[r] < 0){
l ++ ;
}else{
r -- ;
}
}
}
return res;
}
};
18. 四数之和(排序 + 双指针,同三数之和)
分析:时间复杂度O(
n
3
n^{3}
n3),比四数相加II麻烦在去重处理!
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
int n = nums.size();
sort(nums.begin(), nums.end());
for(int i = 0;i < n ; i++ )
for(int j = i + 1;j < n ;j ++ )
{
// 去重
if(i != 0 && nums[i] == nums[i - 1]) break;
if(j != i + 1 && nums[j] == nums[j - 1]) continue; // 这里是continue
int l = j + 1,r = n - 1;
while(l < r)
{
if(nums[i] + nums[j] + nums[l] + nums[r] == target){
res.push_back({nums[i], nums[j], nums[l], nums[r]});
l ++ ,r -- ;
while(l < r && nums[l] == nums[l - 1]) l ++ ;
while(l < r && nums[r] == nums[r + 1]) r --;
}else if(nums[i] + nums[j] + nums[l] + nums[r] < target) l ++ ;
else r -- ;
}
}
return res;
}
};