leetcode hot 100
- 哈希
- 1.字母异位词分组
- 2.最长连续序列
- 双指针
- 1.盛最多水的容器
- 2.和为 K 的子数组
- 数组
- 1.除自身以外数组的乘积
哈希
1.字母异位词分组
49. 字母异位词分组
方法一:排序
由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序之后的字符串作为哈希表的键。
unordered_map<string, vector<string>>
主要是理解,key是排序后的字母序列,value是vector,存放了多个string
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
int n = strs.size();
unordered_map<string, vector<string>> map;
for (auto s : strs) {
string tmp = s;
sort(tmp.begin(), tmp.end());
map[tmp].emplace_back(s);
}
vector<vector<string>> ans;
for (unordered_map<string, vector<string>>::iterator it = map.begin(); it != map.end(); it++) {
ans.emplace_back(it->second);
}
return ans;
}
};
2.最长连续序列
128. 最长连续序列
重要的是思路,
1.用set去除重复数
2.找到一个序列起点最小的数num,开始在容器里while找num++是否存在,如果存在那么len++
3.怎么找到最小数,也不能说是最小数,是一个连续数种的最小数,num - 1如果存在那么他就不是连续的最小数
unordered_set<int> mp;
...
for (auto num: mp) {
if (mp.count(num -1)) continue;
int curnum = num;
int len = 1;
while (mp.count(curnum + 1)) {
curnum += 1;
len += 1;
}
longestlen = max(len, longestlen);
}
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
int res = 0; // 记录最长连续序列的长度
unordered_set<int> num_set(nums.begin(), nums.end()); // 记录nums中的所有数值
int seqLen;
for(int num: num_set){
// 如果当前的数是一个连续序列的起点,统计这个连续序列的长度
if(!num_set.count(num - 1)){
seqLen = 1; // 连续序列的长度,初始为1
while(num_set.count(++num))seqLen++; // 不断查找连续序列,直到num的下一个数不存在于数组中
res = max(res, seqLen); // 更新最长连续序列长度
}
}
return res;
}
};
双指针
1.盛最多水的容器
11. 盛最多水的容器
两个for循环找最大值会超时,那么就有小心机,如果当前高度不比之前高,那么答案一定小于之前的值,就不必再循环了
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0, ans = 0, high = 0;
for (; left < height.size() - 1; left++) {
if (height[left] > high) high = height[left];
if (height[left] < high) continue;
for (int right = left + 1; right < height.size(); right++) {
int tmp = (right - left) * min(height[left], height[right]);
ans = max(ans, tmp);
}
}
return ans;
}
};
正经的比较快的算法是,两端向中间移动,每次移动较小的边,计算最大值
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0, r = height.size() - 1;
int ans = min(height[l], height[r]) * (r - l);
while (l < r) {
if (height[l] < height[r]) {
l++;
} else {
r--;
}
ans = max(ans, (r - l) * min(height[l], height[r]));
}
return ans;
}
};
2.和为 K 的子数组
560. 和为 K 的子数组
题目需要注意的是和为K的子数组,那么:
1.用pre[i]表示num[0]到num[i]的和
2.num[i + 1]到num[j]的和为 pre[j] - pre[i]
3.其和为k, 那么,nums[j]位置需要找到和为pre[j] - k的前缀和
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int, int> mp;
mp[0] = 1;
int pre = 0, ans = 0;
for (auto &c : nums) {
pre += c;
if (mp.count(pre - k)) {
ans += mp[pre - k];
}
mp[pre]++;
}
return ans;
}
};
数组
1.除自身以外数组的乘积
238. 除自身以外数组的乘积
还是想用前缀和做,计算num[i]的乘积,就是计算pre[i -1] * 后缀和
但是超时了
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> ans;
int pre = 0;
for (int i = 0; i < nums.size(); i++) {
if (i == 0)
pre = 1;
else
pre *= nums[i - 1];
int tmp = pre;
for (int j = i + 1; j < nums.size(); j++) {
tmp *= nums[j];
}
ans.emplace_back(tmp);
}
return ans;
}
};
前缀和 + 后缀和
前缀和:
1.i从0开始向后
2.ans[i] = pre;
3.pre *= nums[i];
先把pre[i -1]放到ans[i],再乘
后缀和:
1.i从size()-1开始向前
2.ans[i] *= sum2;
3.sum2 *= nums[i];
前缀乘以back[i+1]到back.end() - 1
https://blog.csdn.net/qq_43553082/article/details/118620364
vector在还没有分配任何空间时还不能像数组一样用下标形式去访问vector的(v[0]也不行)!!!否则编译通过但报运行错误runtime error!
如vector创建的时候是使用:vector<int> v;
或者vector还是[]的时候(考虑官方测试数据的输入可能为[]的情况,使用[ ]前需要判断一下是否为空)会报错!
如:v[0]=1、if(v[0])、v[0]等情况都会报错
这种情况需要先push_back()或v={1,2}等形式给其分配了空间后,才能用【】形式访问!
本题中:
vector<int> ans(nums.size());
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> ans(nums.size());
int sum = 1;
for (int i = 0; i < nums.size(); i++) {
ans[i] = sum;
sum *= nums[i];
}
int sum2 = 1;
for (int i = nums.size() - 1; i >= 0; i--) {
ans[i] *= sum2;
sum2 *= nums[i];
}
return ans;
}
};