leetcode(4) | 栈和队列

目录

栈的题目需要注意:每次top和pop前都要对栈进行检测

一、esay

1.Min Stack

2.Valid Parentheses

3. Implement Stack using Queues

二、Mid

1.Evaluate Reverse Polish Notation(逆置波兰表达式)

三、hard

1.Longest Valid Parentheses

2.Largest Rectangle in Histogram


 

栈的题目需要注意:每次top和pop前都要对栈进行检测

栈一般的使用情况是要找出成对的东西时,或者要根据后面的内容对前面进行操作

 

一、esay

1.Min Stack

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

  • push(x) -- Push element x onto stack.
  • pop() -- Removes the element on top of the stack.
  • top() -- Get the top element.
  • getMin() -- Retrieve the minimum element in the stack.

Example:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> Returns -3.
minStack.pop();
minStack.top();      --> Returns 0.
minStack.getMin();   --> Returns -2.

思路:这道最小栈跟原来的栈相比就是多了一个功能,可以返回该栈的最小值。使用两个栈来实现,一个栈来按顺序存储push进来的数据,另一个用来存出现过的最小值。

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        if(s2.empty() || s2.top() >= x) s2.push(x);
        s1.push(x);
    }
    
    void pop() {
        if(s1.top() == s2.top()) s2.pop();
        s1.pop();
    }
    
    int top() {
        return s1.top();
    }
    
    int getMin() {
        return s2.top();
    }
    
private:
    stack<int> s1,s2;
};

 

2.Valid Parentheses

Given a string containing just the characters '('')''{''}''[' and ']', determine if the input string is valid.

An input string is valid if:

  1. Open brackets must be closed by the same type of brackets.
  2. Open brackets must be closed in the correct order.

Note that an empty string is also considered valid.

Example 1:

Input: "()"
Output: true

Example 2:

Input: "()[]{}"
Output: true

Example 3:

Input: "(]"
Output: false

Example 4:

Input: "([)]"
Output: false

Example 5:

Input: "{[]}"
Output: true

思路:这题不难,使用栈就可以解决,需要注意的是,最后对于栈为空的判断,为空才为true

class Solution {
public:
    bool isValid(string s) {
        stack<char> s1;
        for(char c:s){
            if(c=='(' || c=='{' || c=='[') s1.push(c);
            else{
                // 注意这一步的检查
                if(s1.empty()) return false;
                char c2 = s1.top();
                if((c==')' && c2=='(') || (c==']' && c2=='[') || (c=='}' && c2=='{')) s1.pop();
                else return false;
            }
        }
        // 注意检查
        return s1.empty();
    }
};

 

3. Implement Stack using Queues

Implement the following operations of a stack using queues.

  • push(x) -- Push element x onto stack.
  • pop() -- Removes the element on top of the stack.
  • top() -- Get the top element.
  • empty() -- Return whether the stack is empty.

用队列来模拟栈,这里其实无论怎样写,每次栈pop的时候都对需要重载一次,复杂度为n

我的写法:用了两个栈

class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {
        
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        if(q1.empty()) q2.push(x);
        else q1.push(x);
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int res;
        if(q1.empty()){
            while(!q2.empty()) {
                res = q2.front();
                q2.pop();
                if(!q2.empty()) q1.push(res);
            }
            return res;
        }
        else{
            while(!q1.empty()) {
                res = q1.front();
                q1.pop();
                if(!q1.empty()) q2.push(res);
            }
            return res;
        }
    }
    
    /** Get the top element. */
    int top() {
        int res;
        if(q1.empty()){
            while(!q2.empty()) {
                res = q2.front();
                q1.push(res);
                q2.pop();
            }
            return res;
        }
        else{
            while(!q1.empty()) {
                res = q1.front();
                q2.push(res);
                q1.pop();
            }
            return res;
        }
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        if(q1.empty() && q2.empty()) return true;
        else return false;
    }
    
    private:
    queue<int> q1,q2;
};

网上的写法,其实可以优雅使用一个栈

class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {
        
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        q.push(x);
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int res;
        for(int i = 0;i < q.size()-1;i++){
            res = q.front();
            q.push(res);
            q.pop();
        }
        res = q.front();
        q.pop();
        return res;
    }
    
    /** Get the top element. */
    int top() {
        int res;
        for(int i = 0;i < q.size();i++){
            res = q.front();
            q.push(res);
            q.pop();
        }
        return res;
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        if(q.empty()) return true;
        else return false;
    }
    
private:
queue<int> q;
};

 

二、Mid

1.Evaluate Reverse Polish Notation(逆置波兰表达式)

Evaluate the value of an arithmetic expression in Reverse Polish Notation.

Valid operators are +-*/. Each operand may be an integer or another expression.

Note:

  • Division between two integers should truncate toward zero.
  • The given RPN expression is always valid. That means the expression would always evaluate to a result and there won't be any divide by zero operation.

Example 1:

Input: ["2", "1", "+", "3", "*"]
Output: 9
Explanation: ((2 + 1) * 3) = 9

Example 2:

Input: ["4", "13", "5", "/", "+"]
Output: 6
Explanation: (4 + (13 / 5)) = 6

Example 3:

Input: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]
Output: 22
Explanation: 
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

思路:注意是把计算的中间结果继续入栈,而不是在外面判断,开始给我整晕了...

class Solution {
public:  
    int evalRPN(vector<string>& tokens) {        
        stack<int> s;
        int res = 0,m,n;
        if(tokens.size() == 1) return stoi(tokens[0]);
        
        for(int i = 0;i < tokens.size();i++){
            if (tokens[i] != "+" && tokens[i] != "-" && tokens[i] != "*" && tokens[i] != "/") {
                s.push(stoi(tokens[i]));
            }
            else{
                n = s.top();
                s.pop();
                m = s.top();
                s.pop();
                if(tokens[i] == "/") res = m/n;
                else if(tokens[i] == "*") res = m*n;
                else if(tokens[i] == "+") res = m+n;
                else if(tokens[i] == "-") res = m-n;
                s.push(res);
             } 
        }
        return s.top();
    }
};

 

三、hard

1.Longest Valid Parentheses

Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.

Example 1:

Input: "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()"
Example 2:
Input: ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()"

思路:这题确实比较难,我没考虑到的问题是“(()(()”和“)())()”这两种情况,即对于中间被打断,应该怎样处理,单纯使用栈,只能统计合法()的个数,但是统计不了连续合法括号的个数。参考网上的写法,对于多余 ) 的情况,使用一个start来作为起始位置记录,start=i+1;对于多余 ( 的情况,用栈顶的 i 下标作为起始。

class Solution {
public:
    int longestValidParentheses(string s) {
        stack<int> s1;
        int start = 0,max_cnt=0;
        for(int i = 0;i < s.size();i++){
            if(s[i]=='(') s1.push(i);
            else if(s[i]==')'){
                if(s1.empty()) start = i+1;
                else {
                    s1.pop();
                    // s1为空时,使用start作为起始,非空时使用栈顶作为起始
                    max_cnt = s1.empty()? max(max_cnt,i-start+1):max(max_cnt,i-s1.top());
                }
            }
        }
        return max_cnt;
    }
};

 

2.Largest Rectangle in Histogram

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

 


Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].

 


The largest rectangle is shown in the shaded area, which has area = 10 unit.

 

Example:

Input: [2,1,5,6,2,3]
Output: 10

思路:这题我真的除了暴力破解没啥思路,下面这种方法是参考网上写的剪枝

遍历数组,每找到一个局部峰值(而不是每次都往前查找,这里做了剪枝,因为局部峰值已经是局部最高点,所以往前计算获得的面积是局部最大的,我们比较的是每次的局部峰值处算出的面积到底哪次最大),然后向前遍历所有的值,算出共同的矩形面积,每次对比保留最大值

算出共同的矩形面积不要在heights[i+1]<heights[i]在内部判断后计算,否则如果是持续递增的直方图,就会失去计算机会

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int start = 0,res=0,s,now_max,minH;
        for(int i = 0;i < heights.size();i++){
            // 这里注意i从0开始,与后一位比较,从1开始会略过heights=[1]这种情况
            // 同时判断i是否到末尾,到末尾则往下计算面积
            //而不是heights[i+1]<heights[i]在内部判断,否则如果是持续递增的直方图,就会失去计算机会
            if(i+1 < heights.size() && heights[i+1]>=heights[i]){
                continue;
            }
            now_max = 0;
            minH = heights[i];
            for(int j = i;j >= 0;j--){
                minH = min(minH,heights[j]);
                s = (i-j+1)*minH;
                now_max = s>now_max? s:now_max;
            }
            res = res>now_max? res:now_max;
            start = i+1;
        }
        return res;
    }
};

维护一个栈,用来保存递增序列(这样往回计算的时候,肯定是比前一个小的),相当于上面那种方法的找局部峰值。我们可以看到,直方图矩形面积要最大的话,需要尽可能的使得连续的矩形多,并且最低一块的高度要高。有点像木桶原理一样,总是最低的那块板子决定桶的装水量。那么既然需要用单调栈来做,首先要考虑到底用递增栈,还是用递减栈来做。我们想啊,递增栈是维护递增的顺序,当遇到小于栈顶元素的数就开始处理,而递减栈正好相反,维护递减的顺序,当遇到大于栈顶元素的数开始处理。那么根据这道题的特点,我们需要按从高板子到低板子的顺序处理,先处理最高的板子,宽度为1,然后再处理旁边矮一些的板子,此时长度为2,因为之前的高板子可组成矮板子的矩形 ,因此我们需要一个递增栈,当遇到大的数字直接进栈,而当遇到小于栈顶元素的数字时,就要取出栈顶元素进行处理了,那取出的顺序就是从高板子到矮板子了,于是乎遇到的较小的数字只是一个触发,表示现在需要开始计算矩形面积了,为了使得最后一块板子也被处理,这里用了个小trick,在高度数组最后面加上一个0,这样原先的最后一个板子也可以被处理了。由于栈顶元素是矩形的高度,那么关键就是求出来宽度,那么跟之前那道Trapping Rain Water一样,单调栈中不能放高度,而是需要放坐标。由于我们先取出栈中最高的板子,那么就可以先算出长度为1的矩形面积了,然后再取下一个板子,此时根据矮板子的高度算长度为2的矩形面积,以此类推,知道数字大于栈顶元素为止,再次进栈

单调栈的套路,对后面的数来说,前面的序列的“坎”(其他题里面也可能是“坑”)是无效的信息,每次更新栈都可以弹出去,同时这个题里面,被弹出去的“坎”能形成的最大面积正好就是 (j - i) * height

class Solution {
public:
    int largestRectangleArea(vector<int> &height) {
        int res = 0;
        stack<int> st;
        height.push_back(0);
        for (int i = 0; i < height.size(); ++i) {
            if (st.empty() || height[st.top()] < height[i]) {
                st.push(i);
            } else {
                int cur = st.top(); st.pop();
                res = max(res, height[cur] * (st.empty() ? i : (i - st.top() - 1)));
                --i;
            }     
        }
        return res;
    }
};

同理:Trapping Rain Water 收集雨水

维护一个递减栈,遍历高度,如果此时栈为空,或者当前高度小于等于栈顶高度,则把当前高度的坐标压入栈,注意我们不直接把高度压入栈,而是把坐标压入栈,这样方便我们在后来算水平距离。当我们遇到比栈顶高度大的时候,就说明有可能会有坑存在,可以装雨水。此时我们栈里至少有一个高度,如果只有一个的话,那么不能形成坑,我们直接跳过,如果多余一个的话,那么此时把栈顶元素取出来当作坑,新的栈顶元素就是左边界,当前高度是右边界,只要取二者较小的,减去坑的高度长度就是右边界坐标减去左边界坐标再减1,二者相乘就是盛水量啦

class Solution {
public:
    int trap(vector<int>& height) {
        stack<int> s;
        int res = 0,i = 0,now,bound;
        // 这里不用for循环,因为只有在push以后才需要i++
        while(i < height.size()){
            if(s.empty() || height[i] <= height[s.top()]) s.push(i++);
            else{
                now = s.top();
                s.pop();
                // 注意这里栈空的判断,空就不继续计算
                if(s.empty()) continue;
                // 栈顶和i处较小的那个
                bound = min(height[i],height[s.top()]);
                res += (bound - height[now]) * (i-s.top()-1);
            }
        }
        return res;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值