文章目录
哈希
两数之和
题目
代码
cpp
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
// 方法1:暴力搜索
for(int i = 0; i < nums.size() - 1; i++)
{
for(int j = i + 1; j < nums.size(); j++)
{
if(nums[i] + nums[j] == target)
{
return {i, j};
}
}
}
return {};
}
};
cpp
// 方法二:哈希表
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hash;
for(int i = 0; i < nums.size(); i++)
{
int t = target - nums[i];
if(hash.count(t)) return {hash[t], i};
hash[nums[i]] = i;
}
return {};
}
};
字母异位词分组
题目
代码
cpp
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> hash;
// 1. 把所有的字母异位词分组
for(auto s : strs)
{
string tmp = s;
sort(tmp.begin(), tmp.end());
hash[tmp].push_back(s);
}
// 2. 把结果提取出来
vector<vector<string>> ret;
for(auto& [x, y] : hash)
{
ret.push_back(y);
}
return ret;
}
};
最长连续序列
题目
代码
cpp
// 这个代码是我自己写的,逻辑跟下面的代码是一样的
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> st(nums.begin(), nums.end());
int ret = 0;
for(auto& x : st)
{
if(st.count(x - 1)) continue;
int y = x + 1;
while(st.count(y))
{
y++;
}
ret = max(y - x, ret);
}
return ret;
}
};
cpp
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
int ans = 0;
unordered_set<int> st(nums.begin(), nums.end());
for(int x : st)
{
if(st.contains(x - 1))
continue;
int y = x + 1;
while(st.contains(y))
y++;
ans = ranges::max(ans, y - x);
}
return ans;
}
};
双指针
移动零
题目
代码
cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int cur = 0, dest = -1;
while(cur < nums.size())
{
if(nums[cur])
swap(nums[cur++], nums[++dest]);
else
cur++;
}
}
};
盛最多水的容器
题目
代码
cpp
// 我第二遍自己写出来的代码,方法跟下面的代码是一样的
class Solution {
public:
int maxArea(vector<int>& height) {
int n = height.size(), ans = 0;
int left = 0, right = n - 1;
while(left < right)
{
int h = min(height[left], height[right]);
ans = max(ans, (right - left) * h);
height[left] < height[right] ? left++ : right--;
}
return ans;
}
};
cpp
class Solution {
public:
int maxArea(vector<int>& height) {
int n = height.size(), ans = 0;
int left = 0, right = n - 1;
while(left < right)
{
int a = min(height[left], height[right]);
ans = max(ans, a * (right - left));
if(height[left] < height[right])
left++;
else
right--;
}
return ans;
}
};
三数之和
题目

代码
cpp
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++)
{
int j = i + 1, k = n - 1;
// 在解决"三数之和"问题时,为了避免重复的三元组,需要跳过重复的起始数字。
//例如,对于数组 [1, 1, 2, -3, 0],如果第一个数字 1 已经被用作起始数字进行过遍历,
//那么第二个 1 再次作为起始数字时,会产生重复的三元组。
if(i && nums[i] == nums[i - 1]) continue;
if(nums[i] + nums[i + 1] + nums[i + 2] > 0) break;
if(nums[i] + nums[n - 2] + nums[n - 1] < 0) continue;
while(j < k)
{
int s = nums[i] + nums[j] + nums[k];
if(s < 0) j++;
else if(s > 0) k--;
else
{
ans.push_back({nums[i], nums[j], nums[k]});
for(j++; j < k && nums[j] == nums[j - 1]; j++);
for(k--; j < k && nums[k] == nums[k + 1]; k--);
}
}
}
return ans;
}
};
接雨水
题目

代码
cpp
// 法一:前后缀分解
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size(), ans = 0;
// pre_max[i] 表示从 height[0] 到 height[i] 的最大值
// suf_max[i] 表示从 height[i] 到 height[n-1] 的最大值
vector<int> pre_max(n), suf_max(n);
pre_max[0] = height[0];
suf_max[n - 1] = height[n - 1];
for(int i = 1; i < n; i++)
pre_max[i] = max(pre_max[i - 1], height[i]);
for(int j = n - 2; j >= 0; j--)
suf_max[j] = max(suf_max[j + 1], height[j]);
// 累加每个水桶能接多少水
for(int i = 0; i < n; i++)
ans += min(pre_max[i], suf_max[i]) - height[i];
return ans;
}
};
// 时间复杂度:O(n),其中 n 为 height 的长度。
// 空间复杂度:O(n)。
cpp
// 法二:相向双指针
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0, left = 0, right = height.size() - 1, pre_max = 0, suf_max = 0;
while (left < right) {
pre_max = max(pre_max, height[left]);
suf_max = max(suf_max, height[right]);
ans += pre_max < suf_max ? pre_max - height[left++] : suf_max - height[right--];
}
return ans;
}
};
cpp
// 法三:单调栈方法
class Solution {
public:
int trap(vector<int>& height) {
stack<int> st;
int n = height.size();
int ans = 0;
for(int i = 0; i < n; i++)
{
while(!st.empty() && height[i] >= height[st.top()])
{
// 底部的高度
int bottom_h = height[st.top()];
st.pop();
if(st.empty())
{
break;
}
// 左边界
int left = st.top();
// 高度 = (左右边界的最小值) - 底部的高度
int h = min(height[left], height[i]) - bottom_h; // 面积的高
// 面积
ans += h * (i - left - 1);
}
// 入栈
st.push(i);
}
return ans;
}
};
// 时间复杂度O(n)
// 空间复杂度O(min(n,U)) U=max(height)−min(height)+1
滑动窗口
无重复字符的最长子串
题目

代码
cpp
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> ci;
int ret = 0;
for(int left = 0, right = 0; right < s.size(); right++)
{
ci[s[right]]++;
while(ci[s[right]] > 1)
ci[s[left++]]--;
ret = max(ret, right - left + 1);
}
return ret;
}
};
找到字符串中所有字母异位词
题目

代码
cpp
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int m = p.size(), n = s.size(), hash1[26] = {0}, hash2[26] = {0};
vector<int> ret;
for(auto c : p) hash2[c - 'a']++;
for(int left = 0, right = 0, count = 0; right < n; right++)
{
hash1[s[right] - 'a']++;
if(hash1[s[right] - 'a'] <= hash2[s[right] - 'a']) count++;
if(right - left + 1 > m)
{
if(hash1[s[left] - 'a'] <= hash2[s[left] - 'a']) count--;
hash1[s[left] - 'a']--;
left++;
}
if(count == m) ret.push_back(left);
}
return ret;
}
};
回溯
电话号码的字母组合
题目
思路
代码
bash
class Solution {
vector<string> ret;
string hash[10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
string path;
public:
vector<string> letterCombinations(string digits) {
// 判断边界(特殊情况)
if(digits.size() == 0) return ret;
dfs(digits, 0);
return ret;
}
void dfs(string digits, int pos)
{
//递归出口
if(pos == digits.size())
{
ret.push_back(path);
return;
}
//遍历pos对应的hash中的位置
for(auto ch : hash[digits[pos] - '0'])
{
path.push_back(ch);//先把这个字符加入到path
dfs(digits, pos + 1);//递归:深度优先遍历
path.pop_back();// 恢复现场,回溯到初始,去另一个分支继续迭代
}
}
};
括号生成
题目
思路
代码
cpp
class Solution {
int left, right, n; // left记录左括号数量,right记录右括号数量,n记录总的括号对数
string path; // path记录每次递归的字符串
vector<string> ret; // ret保存最终的return结果
public:
vector<string> generateParenthesis(int _n) {
// 把传入的_n的值赋值给全局变量n
n = _n;
dfs();
return ret;
}
void dfs()
{
// 递归出口
if(right == n)
{
ret.push_back(path);
return;
}
// 判断是否可以加左括号
if(left < n)
{
path.push_back('('); left++;
dfs();
path.pop_back(); left--; // 恢复现场
}
// 判断是否可以加右括号
if(right < left)
{
path.push_back(')'); right++;
dfs();
path.pop_back(); right--; // 恢复现场
}
}
};
组合总和
题目
思路
代码
cpp
// 解法一:按位置来,每个位置 每个nums[i]都试一下
class Solution {
// 把整型参数不写成全局变量,而是写到dfs的参数,可以避免回溯;
int aim; // 存储目标值的 全局变量
vector<int> path; // 存储单次递归的结果
vector<vector<int>> ret; // 存储最终return的结果
public:
vector<vector<int>> combinationSum(vector<int>& nums, int target) {
aim = target;
dfs(nums, 0, 0);
return ret;
}
// pos为nums中元素的位置,从第一个位置开始,直到最后一个位置
// sum为当前递归路径的和
void dfs(vector<int>& nums, int pos, int sum)
{
// 递归终止条件一,完成题目要求的条件
if(sum == aim)
{
ret.push_back(path);
return;
}
// 递归终止条件二,超范围或不满足题意
if(sum > aim || pos > nums.size()) return;
// 循环遍历
for(int i = pos; i < nums.size(); i++)
{
path.push_back(nums[i]);
dfs(nums, i, sum + nums[i]); // 第二个参数是i,不是i加一,因为元素可以重复;
// 第二个元素是sum + nums[i],因为这里没有改变sum的值,所以sum不用恢复现场
path.pop_back();
}
}
};
cpp
// 方法二:先选nums[0],再选nums[1], 再选nums[2], 再选......
class Solution {
// 把整型参数不写成全局变量,而是写到dfs的参数,可以避免回溯;
int aim; // 存储目标值的 全局变量
vector<int> path; // 存储单次递归的结果
vector<vector<int>> ret; // 存储最终return的结果
public:
vector<vector<int>> combinationSum(vector<int>& nums, int target) {
aim = target;
dfs(nums, 0, 0);
return ret;
}
// pos为nums中元素的位置,从第一个位置开始,直到最后一个位置
// sum为当前递归路径的和
void dfs(vector<int>& nums, int pos, int sum)
{
// 递归终止条件一,完成题目要求的条件
if(sum == aim)
{
ret.push_back(path);
return;
}
// 递归终止条件二,超范围或不满足题意
if(sum > aim || pos == nums.size()) return;
// 枚举个数
for(int k = 0; k * nums[pos] <= aim; k++)
{
// 当个数不是0的时候,才加入到path中
if(k) path.push_back(nums[pos]);
dfs(nums, pos + 1, sum + k * nums[pos]);
}
//恢复现场
//这里必须,在循环外恢复现场,把加到path的i个nums[pos]全pop掉
for(int i = 1; i * nums[pos] <= aim; i++)
{
path.pop_back();
}
}
};
N皇后
题目
思路

代码
cpp
class Solution {
bool checkCol[10], checkDig1[20], checkDig2[20];
vector<string> path;
vector<vector<string>> ret;
int n;
public:
vector<vector<string>> solveNQueens(int _n) {
n = _n;
path.resize(n);
for(int i = 0; i < n; i++)
{
//是向 path[i] 中追加 n 个 '.' 字符
//string& append(size_t count, char ch);
path[i].append(n, '.');
}
dfs(0);
return ret;
}
void dfs(int row)
{
if(row == n)
{
ret.push_back(path);
return;
}
//遍历本行的每一列
for(int col = 0; col < n; col++)
{
if(!checkCol[col] && !checkDig1[row - col + n] && !checkDig2[row + col])
{
path[row][col] = 'Q';
checkCol[col] = checkDig1[row - col + n] = checkDig2[row + col] = true;
// 下一行递归
dfs(row + 1);
// 恢复现场
path[row][col] = '.';
checkCol[col] = checkDig1[row - col + n] = checkDig2[row + col] = false;
}
}
}
};
单词搜索
题目
思路