刷leetcode hot100返航版--栈和队列5/24

感觉二叉树每一题一个花样可能是因为栈和队列基础没打好,所以选择直接转入栈和队列。

想摆烂,煎熬----5/24

1.有效的括号

20. 有效的括号 - 力扣(LeetCode)

初版代码,有很多问题吧,正确的思路应该是把{ / [ / (压入栈,然后依次去匹配右边的括号

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)

155. 最小栈 - 力扣(LeetCode)

没思路

也是因为对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.字符串解码【没太理解】

394. 字符串解码 - 力扣(LeetCode)

乱了,感觉一个左括号需要一个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.每日温度【单调栈】

739. 每日温度 - 力扣(LeetCode)

往右找比他大并且索引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】【没思路】

84. 柱状图中最大的矩形 - 力扣(LeetCode)

暴力法超时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()){

                。。。
        }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值