栈和队列相关题目总结

232. 用栈实现队列

两个栈占底相邻拼接在一起,入队直接push到右侧栈就行了,出队先看左侧栈有无元素,若无元素需要将右侧栈元素腾到左侧栈中,若有元素直接左侧栈出栈。

class MyQueue {
public:
    stack st1, st2;
    MyQueue() {

    }
    
    void push(int x) {
        st2.push(x);
    }
    
    int pop() {
        if(st1.size()){
            int res = st1.top();
            st1.pop();
            return res;
        }
        else if(st2.size()){
            while(st2.size()){
                int num = st2.top();
                st2.pop();
                st1.push(num);
            }
            int res = st1.top();
            st1.pop();
            return res;
        }
        else{
            //非法操作
        }
        return 0;
    }
    
    int peek() {
        if(st1.size()){
            int res = st1.top();
            return res;
        }
        else if(st2.size()){
            while(st2.size()){
                int num = st2.top();
                st2.pop();
                st1.push(num);
            }
            int res = st1.top();
            return res;
        }
        else{
            //非法操作
        }
        return 0;
    }
    
    bool empty() {
        return !st1.size() && !st2.size();
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */

225. 用队列实现栈

用两个队列来模拟栈,这两个队列任何时刻都是一个为空,一个非空,然后push元素时push到空队列中,再把非空队列元素依次腾到该队列中,这样接下来出队的时候先出刚push进来的元素。出栈时间复杂度O(1),入栈时间复杂度O(n),可以看到复杂度还是挺高的,而且设计思路没有很巧妙,这题更像是为了对应上一题而出的。

class MyStack {
public:
    queue q1, q2;
    MyStack() {

    }
    
    void push(int x) {
        if(q1.size()){
            q2.push(x);
            while(q1.size()){
                q2.push(q1.front());
                q1.pop();
            }
        }
        else{
            q1.push(x);
            while(q2.size()){
                q1.push(q2.front());
                q2.pop();
            }
        }
    }
    
    int pop() {
        if(q1.size()){
            int res = q1.front();
            q1.pop();
            return res;
        }
        else if(q2.size()){
            int res = q2.front();
            q2.pop();
            return res;
        }
        else{
            //非法操作
        }
        return 0;
    }
    
    int top() {
        if(q1.size()){
            int res = q1.front();
            return res;
        }
        else if(q2.size()){
            int res = q2.front();
            return res;
        }
        else{
            //非法操作
        }
        return 0;
    }
    
    bool empty() {
        return !q1.size() && !q2.size();
    }
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

155. 最小栈

需要用到两个栈,其中一个栈正常存放数据,另外一个栈是个辅助栈,用来存放最小值。这两个栈同步进行push和pop操作,push的时候正常栈正常push,辅助栈push的是当前最小值和待入栈值中的最小的那个,这样就维护了当前栈中最小值,由于pop的时候也是同步进行pop的,所以辅助栈的栈顶永远都是正常栈中的最小值。

但是这种做法空间消耗大,网上还有一种只需要一个栈的做法,这个栈只保存第i个元素和前i-1个元素中最小值的差值,同时还有一个全局变量mn维护前i个元素的最小值。这样如果栈顶元素为负数,那说明第i个元素比前i-1个元素都小,mn就存的是第i个元素的值,此时top()函数就应该返回mn,此时pop()函数弹出了最小值,所以还需要更新mn为前i-1个元素的最小值,也就是令mn = mn-栈顶值,此时mn就恢复为前i-1个元素最小值了。如果栈顶值为非负,那说明第i个元素并不会更新最小值,所以top()函数返回mn+栈顶值,pop()函数直接弹栈,不需要更新mn值。

//双栈版
class MinStack {
public:
    stack st1, st2;
    MinStack() {
        st2.push(0x7fffffff);
    }
    
    void push(int val) {
        st1.push(val);
        st2.push(min(val, st2.top()));
    }
    
    void pop() {
        st1.pop();
        st2.pop();
    }
    
    int top() {
        return st1.top();
    }
    
    int getMin() {
        return st2.top();
    }
};
//单栈版
class MinStack {
public:
    stack st;
    long long mn = 0x7fffffff;
    MinStack() {

    }
    
    void push(int val) {
        long long diff = val-mn;
        st.push(diff);
        if(diff < 0) mn = val;
    }
    
    void pop() {
        if(st.top() > 0) st.pop();
        else{
            mn = mn-st.top();
            st.pop();
        }
    }
    
    int top() {
        if(st.top() > 0) return st.top()+mn;
        else return mn;
    }
    
    int getMin() {
        return mn;
    }
};

/**
 * 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();
 */

20. 有效的括号

经典的栈应用,如果遇到左括号就入栈,如果遇到右括号就出栈,出现以下三种情况就判定为不匹配括号:1、遇到右括号但是栈空。2、遇到右括号但和栈顶左括号不是同类括号。3、遍历完括号序列后栈中仍有左括号。

class Solution {
public:
    bool check(char a, char b){
        if(a == '(' && b == ')') return true;
        if(a == '{' && b == '}') return true;
        if(a == '[' && b == ']') return true;
        return false;
    }
    bool isValid(string s) {
        stack st;
        for(int i = 0; i < s.size(); i++){
            if(s[i] == '(' || s[i] == '{' || s[i] == '[')
                st.push(s[i]);
            else{
                if(st.empty()) return false;
                char top = st.top();
                st.pop();
                if(!check(top, s[i])) return false;
            }
        }
        if(st.size()) return false;
        return true;
    }
};

739. 每日温度

找每个元素右边第一个比它大的,典型的单调栈。可以维护一个单调不增栈,某个元素入栈时弹出栈中所有比它小的元素,这时候就可以更新这些被弹出元素右边第一个比其大的元素了。

class Solution {
public:
    vector dailyTemperatures(vector& temperatures) {
        stack st;
        vector res(temperatures.size());
        for(int i = 0; i < temperatures.size(); i++){
            while(st.size() && temperatures[i] > temperatures[st.top()]){
                res[st.top()] = i;
                st.pop();
            }
            st.push(i);
        }
        while(st.size()){
            res[st.top()] = st.top();
            st.pop();
        }
        for(int i = 0; i < res.size(); i++)
            res[i] = res[i]-i;
        return res;
    }
};

224. 基本计算器

因为字符串里只涉及括号和加减运算,所以这道题实现起来不必中缀转后缀表达式,因为只有加减所以可以直接展开括号,比如5 - ( 3 + 2 ) 可以展为5 - 3 - 2,这里的括号就不具有优先级了,所以整体思路就是从左到右读数字,然后判断它的正负性,再累加起来就行了。如果没有括号,那一个数字的正负完全由前面的符号确定,但有了括号就还需要考虑一点,如果括号前是负号那展开括号后其中的所有符号都会取反,所以就需要一个栈来维护括号前的符号,遇到左括号就把前面符号入栈,遇到右括号就出栈,然后再维护一个变量neg_num记录栈中负号个数。这时候就可以进行正负性判断了,如果遇到了一个正号,那就看下此时neg_num的奇偶性,如果neg_num为偶数那就不变还是正号,如果为奇数那就变号为负。如果遇到的是负号也同理。

由于展开了括号,括号带来的计算优先级也就不存在了,所以可能会出现溢出int范围的情况,题目里说的运算结果在int范围内是在有括号参与的正常运算前提下,比如-2^31 - ( 5 - 6 ),正常运算的话是-2^31 + 1,并不会溢出int,但展开括号就是-2^31 - 5 + 6,这时候就会溢出int了。

class Solution {
public:
    int calculate(string s) {
        stack st;
        long long neg_num = 0, sign = 1, ans = 0;
        char last = '+';
        for(int i = 0; i < s.size(); i++){
            if(s[i] == '+'){
                if(neg_num&1) sign = -1;
                else sign = 1;
                last = s[i];
            }
            else if(s[i] == '-'){
                if(neg_num&1) sign = 1;
                else sign = -1;
                last = s[i];
            }
            else if(s[i] == '('){
                st.push(last);
                if(last == '-') neg_num++;
                last = '+';
            }
            else if(s[i] == ')'){
                if(st.top() == '-') neg_num--;
                st.pop();
            }
            else if(s[i] == ' ') continue;
            else{
                long long num = 0;
                while(i < s.size() && s[i] >= '0' && s[i] <= '9'){
                    num = num*10+s[i]-'0';
                    i++;
                }
                i--;
                ans += sign*num;
            }
        }
        return ans;
    }
};

227. 基本计算器 II

类似上一道题,但是这道题里没有括号但加入了乘除运算,而且全是非负数进行运算。同样可以用中缀转后缀解决,但是在题目给出的限制条件下有更简单的方法。乘除法优先运算,所以可以把表达式中的乘除先运算完,然后把结果值放回去,最后最近一趟加减运算就行了。具体实现的话开一个栈会比较方便,遇到加号把后面原数入栈,遇到减号把后面相反数入栈,遇到乘除号就让后面原数和栈顶做对应运算,弹栈再结果入栈。

class Solution {
public:
    int calculate(string s) {
        stack st;
        char op = '+';
        for(int i = 0; i < s.size(); i++){
            if(s[i] == ' ') continue;
            else if(s[i] >= '0' && s[i] <= '9'){
                long long num = 0;
                while(i < s.size() && s[i] >= '0' && s[i] <= '9'){
                    num = num*10+s[i]-'0';
                    i++;
                }
                i--;
                if(op == '+') st.push(num);
                else if(op == '-') st.push(-num);
                else if(op == '*'){
                    num *= st.top();
                    st.pop();
                    st.push(num);
                }
                else{
                    num = st.top()/num;
                    st.pop();
                    st.push(num);
                }
            }
            else{
                op = s[i];
            }
        }
        long long ans = 0;
        while(st.size()){
            ans += st.top();
            st.pop();
        }
        return ans;
    }
};

402. 移掉 K 位数字

贪心地想,为了让删除k位后的数字最小,首先得确保靠前的数字尽可能小。可以通过单调栈维护出每个数字右边第一个比它小的数字,然后遍历数字,考虑当前位置是删除还是保留,如果删除的话那必须一直删到比它小位置,否则删除是没有意义的,设当前位置为i,右边第一个比它小的位置为a[i],当满足a[i]-i >= k时是可以删除的,如果不满足这个条件那就只能保留了。

class Solution {
public:
    string removeKdigits(string num, int k) {
        stack st;
        vector a(num.size());
        for(int i = 0; i < num.size(); i++){
            while(st.size() && num[i] < num[st.top()]){
                a[st.top()] = i;
                st.pop();
            }
            st.push(i);
        }
        while(st.size()){
            a[st.top()] = num.size();
            st.pop();
        }
        string ans = "";
        bool flag = false;
        for(int i = 0; i < a.size(); i++){
            if(a[i]-i > k){
                if(!flag && num[i] != '0' || flag)
                    ans += num[i], flag = true;
            }
            else{
                k -= a[i]-i;
                i = a[i]-1;
            }
        }
        if(ans == "") ans = "0";
        return ans;
    }
};

你可能感兴趣的:(java,开发语言)