感觉二叉树每一题一个花样可能是因为栈和队列基础没打好,所以选择直接转入栈和队列。
想摆烂,煎熬----5/24
1.有效的括号
初版代码,有很多问题吧,正确的思路应该是把{ / [ / (压入栈,然后依次去匹配右边的括号
class Solution {
public:
bool isValid(string s) {
//妙的一点是网栈里加匹配的那半个,而不是原来的半个[感觉不能]
int size = s.size();
stack<char>st;
int i = 0;
while(i!=size){
if(!st.empty()){
char first = st.top();
// st.pop();
if(first == s[i]){
st.pop();
i++;
}else{
i++;
}
continue;//
}
// st.push(s[i]);//说好的放另一半
if(s[i]=='('){
st.push(')');
}else if(s[i]=='{'){
st.push('}');
}else if(s[i]=='['){
st.push(']');
}
i++;//
}//]]]
if(st.empty()){
return true;
}else{
return false;
}
}
};
混乱版本:【p.s.为了减少混乱其实可以for(),这样起码i++不会出错】
class Solution {
public:
bool isValid(string s) {
//妙的一点是往栈里加匹配的那半个,而不是原来的半个[感觉不能]
int size = s.size();
stack<char>st;
int i = 0;
while(i!=size){
if(s[i]=='('){
st.push(')');
}else if(s[i]=='{'){
st.push('}');
}else if(s[i]=='['){
st.push(']');
}else{//右括号
if(!st.empty()){
char first = st.top();
if(first == s[i]){
st.pop();
i++;//忘记了
continue;
}
}
return false;
}
i++;
}//]]]
if(st.empty()){
return true;
}else{
return false;
}
}
};
2.最小栈5/24
本题的目标就是把求栈的min值从O(n)优化为O(1)
没思路
也是因为对C++类的不熟悉
class 类名 {
private: // 私有成员,只能被类内的成员函数访问 数据类型 成员变量;
public: // 公有成员,可以被类外部的代码访问
返回类型 成员函数(); // 成员函数声明
}; // 注意这里有分号
两个栈,一个放当前min数,
用一个栈存储min,空间换时间
class MinStack {//本题的目标就是用两个栈实现获得栈中元素min:O(n)变为O(1)
public:
stack<int> st;
stack<int>min;
MinStack() {
//常数时间内检索到最小元素的栈。
// min.push(1e9);
}
void push(int val) {
st.push(val);
if(min.empty() ||val < min.top()){
min.push(val);
}else{
min.push(min.top());
}
}
void pop() {//
st.pop();
min.pop();
}
int top() {
return st.top();
}
int getMin() {
return min.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(val);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->getMin();
*/
3.字符串解码【没太理解】
乱了,感觉一个左括号需要一个stack
很难区分[[ ]]和 [ ][ ]
但是其实就是满足栈的情况,
难理解的是如何处理——
3[a2[c]]:3a先压入栈,然后2c压入栈,然后弹出
3[a]2[c]:3a先压入栈弹出,2c压入栈弹出
怎么保存之前的str和要输出的str
感觉用的是,过去的str是压在了栈,然后最后得到栈顶+当前循环的数组
????????????——————————————————————————————?
迷惑:如何处理当前重复的和之前压入栈的
#include <stack>
#include <string>
using namespace std;
class Solution {
public:
string decodeString(string s) {
stack<int> numStack; // 存储重复次数
stack<string> strStack; // 存储外层字符串上下文
int currentNum = 0;
string currentStr = "";
for (char c : s) {
if (isdigit(c)) {
currentNum = currentNum * 10 + (c - '0');
} else if (c == '[') {
// 压入当前状态并重置
numStack.push(currentNum);
strStack.push(currentStr);
currentNum = 0;
currentStr = "";
} else if (c == ']') {
// 弹栈并生成重复字符串
int repeat = numStack.top();
numStack.pop();
string outerStr = strStack.top();
strStack.pop();
string temp;
for (int i = 0; i < repeat; ++i) {
temp += currentStr;
}
currentStr = outerStr + temp; // 栈顶+当前要重复的==总要重复的
} else {
currentStr += c;
}
}
return currentStr; // 最终结果在此
}
};
5/27回做,感觉就是:因为每次读[的时候还没读到本轮需要重复的字母,所以就新开一个str:repeat存每次要循环的字符串,然后和strStack的栈顶string融合一下,作为新的repeat,下一次遇到“[”时压入栈【解决了3[a2[bc]]】
很妙,但是很难解释为什么这样做/不这样做
#include <stack>
#include <string>
using namespace std;
//还是不懂,好玄学
class Solution {
public:
string decodeString(string s) {
stack<int> numStack; // 存储重复次数
stack<string> strStack; // 存储外层字符串上下文
int num = 0;
string repeat;//
for(char c:s){
if(c >= '0' && c<='9'){
num = num*10+ c-'0';
}else if(c=='['){
numStack.push(num);
strStack.push(repeat);
num = 0;//
repeat = "";
}else if(c==']'){
string str;
int nums = numStack.top();
numStack.pop();
cout<<nums;
while(nums != 0){
str+=repeat;
nums--;
}
repeat = strStack.top() + str;//str
strStack.pop();//?
}else{
repeat+=c;
cout<<"repeat"<<" "<<repeat<<endl;
}
}
return repeat;
}
};
4.每日温度【单调栈】
往右找比他大并且索引min的数
没思路,暴力O(n^2)
很奇妙,
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
//answer[i] 是指对于第 i 天,下一个更高温度出现在几天后
//暴力O(n……2)
//从右到左
stack<int>st;
vector<int>result(temperatures.size());
for(int i = temperatures.size()-1;i>=0;i--){
if(i==temperatures.size()-1){
result[i] = 0;
st.push(i);
}
int topNum = st.top();
if(temperatures[topNum] > temperatures[i]){
result[i] = topNum-i;
st.push(i);
}else{
// st.pop();
// st.push(i);
// result[i] = 0;
while(!st.empty()){
topNum = st.top();//没更新
if(temperatures[topNum] <= temperatures[i]){
st.pop();
}else{
result[i] = topNum-i;
break;
}
}
st.push(i);
}
}
return result;
}
};
没太理解从左往右,就是根据上图尝试解决,然后一步步推导
vector<int> dailyTemperatures(vector<int>& temperatures) {
stack<int> st;
vector<int> result(temperatures.size(), 0); // Initialize result with zeros and the correct size
for (int i = 0; i < temperatures.size(); i++) {
while (!st.empty() && temperatures[i] > temperatures[st.top()]) {
int topI = st.top();
result[topI] = i - topI;
st.pop();
}
st.push(i);
}
return result;
}
5.柱状图中max的矩形【5/27】【没思路】
暴力法超时O(n^2)
i,j分别代表柱子的左右两端
注意考虑i,j边界,i可以==j
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {//超时
if(heights.size()==1){
return heights[0];//[1]返回什么
}
//眼熟,像是双指针的,不太一样
//[zuo,右] = zuo-右中min的值*长度
//但是至少可以循环遍历,求min
int result = 0;
for(int i = 0;i<heights.size();i++){//不应该到size-1
int min = heights[i];
int max = 0;
int area;
for(int j = i;j<heights.size();j++){//不应该是i+1【0,9】
//存当前min
if(min>heights[j]){
min = heights[j];
}
area = min*(j-i+1);
if(max<area){
max = area;
}
}
if(result < max){
result = max;
}
}
return result;
}
};
优化:单调栈
我们需要确定的是每个柱子左右两边第一个小于它的位置
deepseek版【记得回来做】
class Solution {
public:
int largestRectangleArea(vector<int> &heights) {
int n = heights.size();
vector<int> left(n, -1);
vector<int> right(n, n);
stack<int> st;
for (int i = 0; i < n; i++) {
while (!st.empty() && heights[i] <= heights[st.top()]) {
right[st.top()] = i;//右边的更小
st.pop();
}
if (!st.empty()) {
left[i] = st.top();//左边的更小:入栈前的栈顶
}
st.push(i);
}
int ans = 0;
for (int i = 0; i < n; i++) {
ans = max(ans, heights[i] * (right[i] - left[i] - 1));//记得减一
}
return ans;
}
};
6.接雨水【拖了n天】
https://leetcode.cn/problems/trapping-rain-water
问题:初始的想法还是按照列计算,但是发现按列计算,找每个元素的大于等于的左边边界有问题
【2112】这样第一个1找的是2和1,就考虑不到后面的1怎么填充了
| 【这里没填】 |
| | | |
【有很大问题版本】
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
if (n == 0) return 0;
stack<int> st;
vector<int> left(n, -1); // 左边第一个>=当前柱子的索引
vector<int> right(n, n); // 右边第一个>=当前柱子的索引
// 计算左右边界
for (int i = 0; i < n; i++) {
while (!st.empty() && height[st.top()] <= height[i]) {
right[st.top()] = i; // 当前柱子是栈顶元素的右边界
st.pop();
}
if (!st.empty()) {
left[i] = st.top(); // 栈顶是当前柱子的左边界
}
st.push(i);
}
// 计算雨水 - 确保每个水洼只计算一次
int area = 0;
for (int i = 0; i < n; ) {
if (right[i] == n || left[i] == -1) { // 没有右边界,无法积水
i++;
continue;
}
int L = left[i]; // 水洼左边界(当前柱子)
int R = right[i]; // 水洼右边界
int h = min(height[L], height[R]);
// 计算[L+1, R-1]区间内的雨水
for (int j = L + 1; j < R; j++) {
area += h - height[j];
}
i = R; // 跳过已计算的水洼区间
}
return area;
}
};
还是决定看其他解法,按照行填:
边抄边写版本:【记得回看】
class Solution {//接雨水,自己硬背去吧
public:
int trap(vector<int>& height) {
int ans = 0;
stack<int> st;
int area = 0;
for (int i = 0; i < height.size(); i++) {//左边界放在栈里,自己去找右边界
while(!st.empty() && height[i] >= height[st.top()]){
int mid = st.top();
st.pop();
if(st.empty()){
break;
}
int left = st.top();
area+=(min(height[i],height[left])-height[mid])*(i-left-1);
}
st.push(i);//记得放入
}
return area;
}
};
单调栈:"找什么用什么"的原则(找小栈底是min,找大栈底是max)
场景:找一个序列中比第i个元素小/大的最近的元素
用一个单调栈存储,栈底是max,栈顶是min,每次拿栈顶和元素比较
优化O(N^2)-->O(n)
for(int i = 0;i<size();i++){
while(!st.empty&&h[i] <=or> st.top()){
。。。
}
}