文章目录
- 第一天 随机刷点
- [1. 两数之和](https://leetcode.cn/problems/two-sum/)(哈希)easy
- [79. 单词搜索](https://leetcode.cn/problems/word-search/)(回溯剪枝)mid
- [45. 跳跃游戏 II](https://leetcode.cn/problems/jump-game-ii/)(贪心算法)mid
- [739. 每日温度](https://leetcode.cn/problems/daily-temperatures/) (单调栈)mid
- [994. 腐烂的橘子](https://leetcode.cn/problems/rotting-oranges/)(图论)mid
- [46. 全排列](https://leetcode.cn/problems/permutations/)(回溯递归)mid
- [121. 买卖股票的最佳时机](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/)(贪心)easy
- [3. 无重复字符的最长子串](https://leetcode.cn/problems/longest-substring-without-repeating-characters/)(滑动窗口)mid
- [198. 打家劫舍](https://leetcode.cn/problems/house-robber/)(动态规划)mid
第一天 随机刷点
1. 两数之和(哈希)easy
一趟for循环,遍历数组
如果找不到与之匹配的元素,用哈希表(unordered_map)存储当前元素及其下标
如果找到直接返回下标对
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> hashTable;
for(int i = 0; i < nums.size(); ++i) {
if(hashTable.find(target - nums[i]) == hashTable.end()) {
hashTable[nums[i]] = i;
}
else return {i,hashTable[target-nums[i]]};
}
return {};
}
};
79. 单词搜索(回溯剪枝)mid
按行列i、j下标进行遍历,用k来代表当前匹配是单词第几个字符
对每一个元素,从四个方向执行dfs,
如果找到匹配,将当前访问的元素置空,防止重复访问。接着下一个dfs,递归回去的时候,需要将置空的元素还原
如果找不到,则返回false
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
//遍历每一个字母
rows = board.size();
cols = board[0].size();
for(int i = 0; i < rows; ++i) {
for(int j = 0; j < cols; ++j) {
if(dfs(board, word, i, j, 0)) return true;
}
}
return false;
}
private:
int rows, cols;
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
//判断i、j是否越界,和字符是否匹配
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
//如果已经匹配到单词最后一个字符,则直接返回true
if(k == word.size()-1) return true;
board[i][j] = '\0';
bool ret = dfs(board, word, i+1, j, k+1) || dfs(board, word, i-1, j, k+1) || dfs(board, word, i, j+1, k+1) || dfs(board, word, i, j-1, k+1);
board[i][j] = word[k]; //还原置空的字符
return ret;
}
};
45. 跳跃游戏 II(贪心算法)mid
遍历时,记录一个边界值end;当遍历到end的时候,说明这是从一个坑位二段跳到的所有可能的结束。
比如2,3,1,1,4;在2这里起跳时的边界值end就是第一个1的位置,也就是下标2;到达边界值时,此时maxPos的位置也确定下来了,这样就可以更新步数。因为不可以原地跳,所以到end的时候找到maxPos必定会跳一次。
class Solution {
public:
int jump(vector<int>& nums) {
int step = 0;
int end = 0;
int maxPos = 0;
for(int i = 0; i < nums.size() - 1; ++i) {
maxPos = max(maxPos, i + nums[i]);
if(i == end) { //当i到达边界
end = maxPos;
step++;
}
}
return step;
}
};
例如2,3,1,1,4这个例子
- 当刚开始时,由于end为0,刚起跳时,会找到可跳2格时一段跳的边界,也就是end被赋为2
- 继续遍历3,可以看到,在元素3的位置最多可以跳到元素4,但是没有到边界,此时不用着急下结论,因为maxPos的位置已经被记录下来了,不用着急去记录是否为最简的跳法
- 继续遍历1,只可以跳到第二个1,由于二段跳的最大位置没能达到maxPos,直接被忽略,然而,此时已经访问到了end所指向的边界位置带有记忆功能的maxPos就指出了从元素2这个地方二段跳最多可以到maxPos的位置,而不用去关心是在哪里进行的第二段跳
- 由于访问完元素3和1;这里是end的第二次更新(第一次是从起点起跳的时候),也就是说,在数组[1,2],这个区间内,只会有一次起跳的机会,因此step++可以保证总的跳数最少。
739. 每日温度 (单调栈)mid
给定一个整数数组 temperatures
,表示每天的温度,返回一个数组 answer
,其中 answer[i]
是指对于第 i
天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0
来代替。
示例 1:
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
维护一个栈,从栈底到栈顶记录下标,下标对应的温度递减
从头遍历数组
- 栈空:直接入栈
- 栈不空:
- 栈顶小于当前元素,将栈顶对应的下标在ret数组中记录相差时间,弹栈,继续比较下一个栈顶元素直到找不到比当前元素温度小的
- 栈顶大于或等于当前元素,入栈
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> ret(n);
stack<int> st;
for(int i = 0; i < n; ++i) {
while(!st.empty() && temperatures[st.top()] < temperatures[i]) {
ret[st.top()] = i - st.top();
st.pop();
}
st.push(i);
}
return ret;
}
};
994. 腐烂的橘子(图论)mid
class Solution {
private:
int badTime[10][10]; //全部橙子的腐烂时间
int count = 0;
int dir_x[4] = {0, 0, 1, -1};
int dir_y[4] = {1, -1, 0, 0};
public:
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
memset(badTime, -1, sizeof(badTime)); //将没访问过的格子置为-1
queue<pair<int,int>> badOranges; //坏橘子队列
// 烂橘子存入队列+设置腐烂时间+记录新鲜橘子数
for(int i = 0; i < m; ++i) {
for(int j = 0; j < n; ++j) {
if(grid[i][j] == 2) {
badOranges.emplace(i,j);
badTime[i][j] = 0;
}
else if(grid[i][j] == 1) {
++count;
}
}
}
int ret = 0; //返回的时间
//开始BFS
while(!badOranges.empty()) {
auto [row, col] = badOranges.front(); //取出一个坏橘子
badOranges.pop();
for(int i = 0; i < 4; ++i) {
int dx = row + dir_x[i];//探路
int dy = col + dir_y[i];
// 越界访问,已经访问过,空单元格统统跳过
if(dx < 0 || dx >= m || dy < 0 || dy >= n
|| badTime[dx][dy] != -1 || grid[dx][dy] == 0) {
continue;
}
badTime[dx][dy] = badTime[row][col] + 1;
badOranges.emplace(dx, dy); //变成坏橘子
if(grid[dx][dy] == 1) {
--count;
ret = badTime[dx][dy];
if(count == 0) break;
}
}
}
return count == 0 ? ret : -1;
}
};
46. 全排列(回溯递归)mid
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
思路:
以递归树的方式,以first为交换的第一个元素的下标,交换后将first+1,更新下一个交换的中心。
class Solution {
public:
void backTrack(vector<vector<int>>& ret, vector<int>& output, int first, int len) {
if(first == len) {
ret.emplace_back(output);
return;
}
for(int i = first; i < len; ++i) { //第一层的交换
swap(output[i], output[first]);
backTrack(ret, output, first + 1, len); //更新first
swap(output[i], output[first]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> ret;
backTrack(ret, nums, 0, nums.size());
return ret;
}
};
121. 买卖股票的最佳时机(贪心)easy
简单题,维护一个最小价格和最大利润。这题是只有一次买入和卖出的。
class Solution {
public:
int maxProfit(vector<int>& prices) {
//在最低日买入
int profit = 0;
int minPrice = INT_MAX;
for(int i = 0; i < prices.size(); ++i) {
minPrice = min(minPrice, prices[i]);
profit = max(profit, prices[i]- minPrice);
}
return profit;
}
};
3. 无重复字符的最长子串(滑动窗口)mid
使用unordered_map做哈希表,记录字符所在的下标
如果找到曾经遍历过相同的字符(为什么需要max更新left)
- 如果当前的滑动窗口中有该相同的字符,不加max是可以的
- 如果当前的滑动窗口没有该相同字符,不加max,则会将之前的相同元素和当前遍历的元素之间的所有元素都放进窗口内,使窗口回退(如abba)。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int ret = 0;
int left = -1;
//维护一个哈希表,记录字符所在的下标
unordered_map<char, int> hashTable;
for(int right = 0; right < s.size(); ++right) {
if(hashTable.find(s[right]) != hashTable.end()) {//找到有过
left = max(left,hashTable[s[right]]);
// left = hashTable[s[right]]; 排除会回退的情况
// 比如abba,遍历到最后一个a时,不加max,让left回退
// 加了left可以保证不会额外增长窗口长度
}
ret = max(ret, right -left);
hashTable[s[right]] = right;
}
return ret;
}
};
198. 打家劫舍(动态规划)mid
dp[i] = max(dp[i-2] + nums[i], dp[i-1])
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 0) return 0;
if(n == 1) return nums[0];
vector<int> dp(n,0);
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]);
for(int i = 2; i < n; ++i) {
dp[i] = max(dp[i-2] + nums[i], dp[i-1]);
}
return dp[n-1];
}
};