1. 两数之和(6.19)
解法1:暴力枚举
最简单的思路应该是双指针暴力枚举,用两个for循环寻找匹配的两个数,虽然一定会有解但是还需要在最后返回一个空值,不然编译无法通过
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size();
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums[i] + nums[j] == target) {
return {i, j};
}
}
}
return {};
}
};
解法2:哈希查找
运用哈希表存储nums的index,由于一定有解,所以可以不用设置边界条件,这样的话编译器会认为这是一个死循环,能够成功编译。
如果写了边界条件,必须要在函数末尾返回空值,不然编译器将会报错error: non-void function does not return a value in all control paths
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> m;
for(int j = 0; ;j++){
auto it = m.find(target-nums[j]);
if(it != m.end()){
return {it->second, j};
}
m[nums[j]] = j;
}
}
};
49. 字母异位词分组(6.19)

解法1:哈希分组
因为拥有相同字母的字符串排序后都会变得相同,所以我们将字符串进行排序,相同的放入同一个key中,最后输出即可
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> m;
for(string&s : strs){
string sorted_s = s;
ranges::sort(sorted_s);
m[sorted_s].push_back(s);
}
vector<vector<string>> ans;
ans.reserve(m.size());
for(auto&[_,value] :m){
ans.push_back(value);
}
return ans;
}
};
128. 最长连续序列(6.19)

解法1:哈希表遍历
这种解法比较简单,将nums用set先去重,再将每一个可能是序列开头的数进行遍历,最后比较得到最大值
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
int ans = 0;
unordered_set<int> st(nums.begin(), nums.end());
for(int i : st){
if(st.contains(i-1)){
continue;
}
int y = i+1;
while(st.contains(y)){
y++;
}
ans = max(ans, y - i);
}
return ans;
}
};
解法2:哈希表动态规划
这种解法比较巧妙一点,看了好久才看懂了。
先建立一个空的map,然后遍历nums数组,只有没进入过的数字才会进入map中
然后获取num左边和右边的范围值,最后将其进行赋值。
m[num] = -1;//随便赋值即可,只是为了避免重复数字进入
m[num-left] = curlen;//当left为0时,就是num赋值为len
m[num+right] = curlen;
还有一个踩雷点就是,当num - 1 不在 unordered_map 中,访问 m[num - 1] 会插入一个默认构造的值(即 0)到 map 中。这会改变 map 的状态,导致后续处理出现错误,所以判断这里需要用到find
疑问:为什么map中的value能够时而表示左区间的长度,时而表示右区间的长度呢?
关键在于判断条件上:if (!map.containsKey(num)),这行代码表示num之前并未出现过。那么对于num-1来说,它的value表示的区间就只能是[num-value,num-1],num-1只能是该区间的左边界值,而其它可能的连续区间都会包含num,不符合上述条件;同理,对于num+1来说,它的value表示的区间就只能是[num+1,num+value]
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_map<int,int> m;
int ans = 0;
for(int num : nums){
if(m.find(num) == m.end()){
int left = m.find(num - 1)!=m.end() ? m[num - 1] : 0;
int right = m.find(num + 1)!=m.end() ? m[num + 1] : 0;
int curlen = left + right + 1;
ans = max(ans, curlen);
m[num] = -1;
m[num-left] = curlen;
m[num+right] = curlen;
}
}
return ans;
}
};
283. 移动零(6.22)

解法1:双指针
用快慢指针遍历数组,如果遇到非0就一起往前走,遇到0的话就快指针走,这样当再次遇到非0时就可以与先前的0交换了
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int j = 0;
for(int& x : nums){
if(x){
swap(x,nums[j]);
j++;
}
}
}
};
解法2:模拟栈
模拟栈的解法,先忽略0的存在,先将其他的排列好,最后将差的0在结尾补上
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int stack_size = 0;
for(int&n : nums){
if(n){
nums[stack_size++] = n;
}
}
fill(nums.begin()+stack_size, nums.end(), 0);
}
};
11. 盛最多水的容器(6.27)

解法:双指针
一开始直接用双指针暴力遍历,但是超时了,开始思考看题解:
将指针分别放在首尾,然后进行比较,因为较短的边决定装水的高度,因此我们移动短边才能让装水量有可能变大,于是相向双指针出现了!!!
class Solution {
public:
int maxArea(vector<int>& height) {
int ans = 0;
int l = 0;
int r = height.size()-1;
while(l != r){
if(height[l] < height[r]){
ans = max(ans, (r-l)*height[l]);
l++;
}
else{
ans = max(ans, (r-l)*height[r]);
r--;
}
}
return ans;
}
};
15. 三数之和(6.27)

解法:双指针
一开始做这道题还是有点思路的,先将数组进行排序,这样只要遍历一个数,后面的问题就变成和上一题一样,寻找target值了。如果sum<0,则将left指针往右移动,反之right指针往左移。
注意:①因为题目中要求不能包含重复的三元组,于是三个指针都需要与上一个比较,若相同则跳过。②如果nums[i]+最前面两个值仍大于0,说明后面的也会大于0,直接剪枝。③同理当+最后面两个值还小于0,则继续遍历下一个值。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
int n = nums.size();
for(int i = 0; i < n - 2; i++){
if(i && nums[i] == nums[i-1])
continue;
if(nums[i] + nums[i+1] + nums[i+2] > 0)
break;
if(nums[i] + nums[n-1] + nums[n-2] < 0)
continue;
int l = i + 1;
int r = n - 1;
while(l < r){
int s = nums[i] + nums[l] + nums[r];
if(s < 0){
l++;
}
else if(s> 0){
r--;
}
else{
ans.push_back({nums[i], nums[l], nums[r]});
for (l++; l < r && nums[l] == nums[l - 1]; l++);
for (r--; r > l && nums[r] == nums[r + 1]; r--);
}
}
}
return ans;
}
};
42. 接雨水
解法1:双指针
使用相向双指针,将指针置于首尾,由于水的体积是由短板决定的,因此我们移动短的那一边(短的只能是短边,长的可能变为短边),计算每个柱子的储水量,直到两个指针相遇。
class Solution {
public:
int trap(vector<int>& height) {
int left = 0;
int right = height.size() - 1;
int h_l = 0, h_r = 0;
int ans = 0;
while(left < right){
h_l = max(h_l, height[left]);
h_r = max(h_r, height[right]);
if(h_l < h_r){
ans += h_l - height[left];
left++;
}
else{
ans += h_r - height[right];
right--;
}
}
return ans;
}
};
解法2:前后缀
这种解法思路和上一种是一样的,寻找左右边界的值,这里我们直接用两个循环计算出premax和sufmax,最后计算得出。
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
vector<int> pre_max(n);
pre_max[0] = height[0];
int ans = 0;
for(int i = 1; i < n; i++){
pre_max[i] = max(pre_max[i-1], height[i]);
}
vector<int> suf_max(n);
suf_max[n - 1] = height[n - 1];
for(int i = n - 2; i >= 0; i--){
suf_max[i] = max(suf_max[i + 1], height[i]);
}
for(int i = 1; i < n; i++){
ans += min(suf_max[i], pre_max[i]) - height[i];
}
return ans;
}
};
解法3:单调栈
单调栈适用于:寻找下一个(上一个)的更大(更小)的元素
不同于上述的解法,单调栈是将雨水看作是横向填充的,我们从左开始遍历,直到找到一个比栈顶元素更大的值,这时候一般就会找到左右边界了
我们将栈顶元素弹出,与左右的边界计算雨水体积大小,依次循环下去
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0;
stack<int> st;
for(int i = 0; i < height.size(); i++){
while(!st.empty() && height[st.top()] <= height[i]){
int bottom_h = height[st.top()];
st.pop();
if(st.empty()){
break;
}
int left = st.top();
ans += (i - left -1) * (min(height[i], height[left]) - bottom_h);
}
st.push(i);
}
return ans;
}
};
&spm=1001.2101.3001.5002&articleId=148763318&d=1&t=3&u=7448a824f1df488fb9a0a19e3440e872)
2280

被折叠的 条评论
为什么被折叠?



