题源:LeetCode
550道,每题重复10遍,我就不信记不住了还。
45.跳跃游戏II
贪心,通过局部最优解得到全局最优解。每次在上次能跳到的范围(end)内选择一个能跳的最远的位置(也就是能跳到max_far位置的点)(覆盖了跳得近的点)作为下次的起跳点 。
class Solution {
public:
int jump(vector<int>& nums) {
int step = 0;//跳跃次数
int max_far = 0;//目前能跳到的最远位置
int end = 0; //上次跳级可达范围右边界(下次的最右起跳点)
for(int i = 0; i < nums.size() - 1; i++){
max_far = max(max_far, i + nums[i]);
if(i == end){
//到达上次跳跃能到达的右边界了
end = max_far; // 目前能跳到的最远位置变成了下次起跳位置的右边界,起跳点就是能跳到最远位置的点
step++; // 进入下一次跳跃
}
}
return step;
}
};
1190. 反转每对括号间的子串
对于括号序列相关的题目,通用的解法是使用递归或栈。本题中我们将使用栈解决。
下一层和上一层之间如何跳转。
class Solution {
public:
string reverseParentheses(string s) {
stack<string> stk;
string str;
for(auto & ch : s){
//上下层跳转时才会对栈操作,否则就对当前字符串操作
if(ch == '('){
//下一层
stk.push(str);
str = "";
}else if(ch == ')'){
//上一层
reverse(str.begin(), str.end());
str = stk.top() + str;
stk.pop();
}else {
//这一层
str.push_back(ch);
}
}
return str;
}
};
5. 最长回文子串
动态规划
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
// dp[left][right]标记从i到j是否是回文串
vector<vector<int> > dp(n, vector<int>(n));
string ans;
// length表示判断的字串的长度
// left表示字串的左边起始位置
// right表示字串的右边起始位置
for(int length = 1; length <= n; length++){
//length = 0 是非法的
for(int left = 0; left + length-1 < n; left++){
int right = left + length -1;
// 即字符串长度为1时,矩阵对角线
if(length == 1) dp[left][right] = 1; //边界1
// 字符串长度为2的时候,只需判断两者是否相等
else if(length == 2) dp[left][right] = (s[left] == s[right]); //边界2
else{
// 字符串长度大于等于3之后
// 其是否是回文串取决于当前left和right及更小一号的字符串
// 更新参考值为矩阵表的左下方
dp[left][right] = (s[left] == s[right] && dp[left + 1][right - 1]);
}
// 如果当前left位置到right位置的字串能够构成回文串,并且现在长度大于之前记忆中的子回文串的长度,那么更新回文串!这里也可以优化成记录起始位置和长度的两个int,返回时再截取
if(dp[left][right] && length > ans.size()){
ans = s.substr(left, length );
}
}
}
return ans;
}
};
中心扩展法
class Solution {
public:
pair<int, int> expandAroundCenter(const string& s, int left, int right){
while(left >=0 && right < s.size() && s[left] == s[right]){
left--;
right++;
}
return {
left + 1, right - 1};
}
string longestPalindrome(string s) {
int start(0), end(0);
for(int i = 0; i < s.size(); i++){
auto [left1, right1] = expandAroundCenter(s, i, i);
auto [left2, right2] = expandAroundCenter(s, i, i + 1);
if(right1 - left1 > end - start){
start = left1;
end = right1;
}
if(right2 - left2 > end - start){
start = left2;
end = right2;
}
}
return s.substr(start, end - start + 1);
}
};
739. 每日温度
单调栈
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> ans(n, 0);
stack<int> s;
for(int i = 0; i < n; i++){
while(!s.empty() && temperatures[i] > temperatures[s.top()]){
int previousIndex = s.top();
ans[previousIndex] = i - previousIndex;
s.pop();
}
s.push(i);
}
return ans;
}
};
200. 岛屿数量
class UnionFind{
vector<int> parent;
int n;
public:
UnionFind(int _n) : n(_n), parent(_n){
iota(parent.begin(), parent.end(), 0);
}
int find(int x){
return x == parent[x] ? x : parent[x] = find(parent[x]);
}
void merge(int x, int y){
int rootx = find(x);
int rooty = find(y);
if(rootx == rooty) return;
parent[rooty] = rootx;//把y合并到x上
n--;//合并后集合数量减一
}
bool isConnected(int x, int y){
return find(x) == find(y);
}
int getSize(){
return n;//最开始是m*n个集合
}
};
class Solution {
public:
int dx[4] = {
-1, 1, 0, 0};
int dy[4] = {
0, 0, 1, -1};
int numIslands(vector<vector<char>>& grid) {
int m = grid.size();//行
int n = (m == 0 ? 0 : grid[0].size());//列
if(m == 0 || n == 0) return 0;
vector<int> cnt(2, 0);//计数君,grid[i][j]的值是1或0
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
cnt[grid[i][j]-'0']++;//cnt[0]是grid[][]中0出现的次数。cnt[1]是grid[][]中1出现的次数。
UnionFind uf(m * n);
for(int i = 0; i < m; i++){
//行
for(int j = 0; j < n; j++){
//列
if(grid[i][j] == '0')
continue;
for(int k = 0; k < 4; k++){
int xx = i + dx[k], yy = j + dy[k];
if(xx < 0 || xx >= m || yy < 0 || yy >= n)
continue;
if(grid[xx][yy] == '1'){
uf.merge(i * n + j, xx * n +