leetcode刷题之栈和队列(匹配、逆波兰,优先队列,单调队列)

一、栈与队列的关系

1. 232用栈实现队列

题目

class MyQueue 
{
    
    
public:
    stack<int>stack_in;
    stack<int>stack_out;

    MyQueue() 
    {
    
    

    }
    
    void push(int x) 
    {
    
    
        stack_in.push(x);
    }
    
    int pop()
    {
    
    
        if(stack_out.empty())//空的就是true
        {
    
    
            while(!stack_in.empty())
            {
    
    
                stack_out.push(stack_in.top());
                stack_in.pop();
            }
        }
        int result = stack_out.top();
        stack_out.pop();
        return result;
    }
    int peek() 
    {
    
    
        if(stack_out.empty())//空的就是true
        {
    
    
            while(!stack_in.empty())
            {
    
    
                stack_out.push(stack_in.top());
                stack_in.pop();
            }
        }
        int result=stack_out.top();
        return result;
    }
    
    bool empty() 
    {
    
    
        if (stack_in.empty()&&stack_out.empty())
        {
    
    
            return true;
        }
        else
        {
    
    
            return false;
        }
    }
};

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

2. 255用两个队列实现栈

题目

class MyStack {
    
    
public:
    queue<int> queue1;//这是用来模仿栈的
    queue<int> queue2;
    MyStack() {
    
    

    }
    
    void push(int x) 
    {
    
    
        queue1.push(x);
    }
    
    int pop() 
    {
    
    
        int size=queue1.size();
        size--;
        while(size--)
        {
    
    
            queue2.push(queue1.front());
            queue1.pop();//从头部弹出元素
        }
        int result=queue1.back();
            
        queue1.pop();
        queue1=queue2;
        while (!queue2.empty()) 
        {
    
     // 清空queue_out
            queue2.pop();
        }
       
        return result;
    }
    
    int top() 
    {
    
    
        int result=queue1.back();
        return result;
    }
    
    bool empty() 
    {
    
    
        if(queue1.empty())
        {
    
    
            return true;
        }
        else
        {
    
    
            return false;
        }
    }
};

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

补充:用一个队列实现栈

class MyStack 
{
    
    
public:
    queue<int> que;
    /** Initialize your data structure here. */
    MyStack() {
    
    

    }
    /** Push element x onto stack. */
    void push(int x) 
    {
    
    
        que.push(x);
    }
    /** Removes the element on top of the stack and returns that element. */
    int pop() 
    {
    
    
        int size = que.size();
        size--;
        while (size--) 
        {
    
     // 将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部
            que.push(que.front());
            que.pop();
        }
        int result = que.front(); // 此时弹出的元素就是栈的顶部
        que.pop();
        return result;
    }

    /** Get the top element. */
    int top() 
    {
    
    
        return que.back();
    }

    /** Returns whether the stack is empty. */
    bool empty() 
    {
    
    
        return que.empty();
    }

二、栈与匹配

1.20有效的括号(此题想了很久,最后看了答案)

题目 难度:简单
解题思路:

  1. 先判断字符串大小是否是2的倍数,不是2的倍数一定匹配不上!
  2. 遍历字符串:
    第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false
    第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false
    第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false
  3. 最后判断栈是否为空,空的栈括号能匹配上,不是空的栈匹配不上!
class Solution 
{
    
    
public:
    stack<char> s1;
    bool isValid(string s) 
    {
    
    
        int size=s.length();
        if(size%2!=0)
        {
    
    
            return false;
        }
        //if(size==0) return true;
        for(int i=0;i<size;i++)
        {
    
    
            if(s[i]=='(')
            {
    
    
                s1.push(')');
            }
            else if(s[i]=='{')
            {
    
    
                s1.push('}');
            }
            else if(s[i]=='[')
            {
    
    
                s1.push(']');
            }
            else if (s1.empty() || s1.top() != s[i]) //第三种和第二种
            {
    
    
            	return false;
            }
            else if(s1.top()==s[i]) s1.pop();// st.top() 与 s[i]相等,栈弹出元素
        }
		 // 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
        return s1.empty();

    }
};

2.1047 删除字符串中的所有相邻重复项

题目 难度:简单

class Solution {
    
    
public:
    stack<char> sta;

    string removeDuplicates(string s) 
    {
    
    
        string s_new;
        int size=s.length();
        for(int i=0;i<size;i++)
        {
    
    
            if(sta.empty() || s[i]!= sta.top())
            {
    
    
                sta.push(s[i]);
            }
            else
            {
    
    
                sta.pop();
            }
        }
        
        while(!sta.empty())
        {
    
    
            s_new+=sta.top();
            sta.pop();
        }
        reverse(s_new.begin(), s_new.end()); 
        return s_new;
    }
};

总结:

  • 字符串拼接:
    (1)+
    (2) apeend
  • 字符串反转:

string s;
reverse(s.begin(), s.end());

三、栈之逆波兰

1.150. 逆波兰表达式求值

题目 难度:中等
规则:从左往右遍历这个表达式的每个数字和符号,凡是遇到数字就进栈,遇到符号,就将栈顶的两个数字出栈,进行运算,再将运算结果进栈。

class Solution 
{
    
    
public:
    int evalRPN(vector<string>& tokens) 
    {
    
    
        int size=tokens.size();
       
        stack<int> sta;
        for(int i=0;i<size;i++)
        {
    
    
            if((tokens[i]!="+")&&(tokens[i]!="-")&&(tokens[i]!="*")&&(tokens[i]!="/"))
            {
    
    
                sta.push(stoi(tokens[i]));
            }
            else if(tokens[i]=="+")
            {
    
    
                int a=sta.top();
                sta.pop();
                int b=sta.top();
                sta.pop();
                sta.push(a+b);
            }
            else if(tokens[i]=="-")
            {
    
    
                int a=sta.top();
                sta.pop();
                int b=sta.top();
                sta.pop();
                sta.push(b-a);
            }
            else if(tokens[i]=="*")
            {
    
    
                int a=sta.top();
                sta.pop();
                int b=sta.top();
                sta.pop();
                sta.push(a*b);
            }
            else if(tokens[i]=="/")
            {
    
    
                int a=sta.top();
                sta.pop();
                int b=sta.top();
                sta.pop();
                sta.push(b/a);
            }
        }
        int result=sta.top();
        return result;
    }
};

总结:
字符串与数字之间的转换:

stod()//字符串转double
stof()//字符串转float
stoi()//字符串转int
stol()//字符串转long
stold()//字符串转double
stoll()//字符串转long long

string to_string(待转换的数字)

四、队列之优先队列

  • 什么是优先级队列呢?
    其实就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。

  • 而且优先级队列内部元素是自动依照元素的权值排列。那么它是如何有序排列的呢?
    缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)。

1.347 前 K 个高频元素

题目 难度:简单

  • priority_queue<Type, Container, Functional>
    Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。
    如果不写后两个参数,那么容器默认用的是 vector,比较方式默认用 operator<,也就是优先队列是大顶堆(less),队头元素最大,本题为小顶堆(greater)。

  • emplace、emplace_front、emplace_back分别对应insert、push_front和push_back。

class KthLargest {
    
    
public:
    int K;
    priority_queue<int,vector<int>,greater<int>> que;//小顶堆:从小到大
    KthLargest(int k, vector<int>& nums) 
    {
    
    
        K=k;
        
        int size=nums.size();
        for(int i=0;i<size;i++)
        {
    
    
            que.push(nums[i]);//将nums里面的元素进行从小到大排序
        }
        for(int j=0;j<(size-k);j++)//nums里面的元素只剩k个
        {
    
    
            que.pop();
        }
    }

    int add(int val) 
    {
    
    
        
        que.push(val);
        int size_new=que.size();
        for(int j=0;j<(size_new-K);j++)
        {
    
    
            que.pop();
        }
        return que.top();

    }
};

五、队列之单调队列

  • 单调队列:顾名思义其中所有的元素都是单调的(递增或者递减),承载的技术数据结构是队列,实现是双端队列,队头元素为窗口的最大最小元素。
    队头删除不符合有效窗口的元素,队尾删除不符合最值的候选元素。
  • 但是单调队列不是真正的队列,因为队列都是先进先出,而单调队列不符合FIFO。
  • 删头操作:队头元素出队列。
    去尾操作:队尾元素出队列,待新元素从队尾入队,从队尾开始删除影响队列单调性的元素。
  • 但是要保证:插入必须在去尾之后来。

1.239滑动窗口最大值

题目 难度:困难

class MyQueue {
    
    
public:
    void pop(int value) {
    
    
    }
    void push(int value) {
    
    
    }
    int front() {
    
    
        return que.front();
    }
};

设计单调队列的时候,pop,和push操作要保持如下规则:

  • push(value):如果push的元素value大于入口元素的数值,那么就将队列入口(尾部)的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止。
  • pop(value):如果窗口移除的元素value等于单调队列的出口(头部)元素,那么队列弹出元素,否则不用任何操作。
  • que.front()就可以返回当前窗口的最大值。
class Solution {
    
    
public:

    class MyQueue 
    {
    
    
public:

        deque<int> que; // 使用deque来实现单调队列

        //去尾
        void push(int value) 
        {
    
    
            while(!que.empty()&&value>que.back())
            {
    
    
                que.pop_back();//队尾删除不符合最值的候选元素
            }
            que.push_back(value);//插入必须在去尾后面
        }

        //删头
        void pop(int value) 
        {
    
    
            if(!que.empty()&&value==que.front())
                {
    
    
                    que.pop_front();//队头删除不符合有效窗口的元素
                }
        }
        int front() 
        {
    
    
            return que.front();
        }
};
    vector<int> maxSlidingWindow(vector<int>& nums, int k) 
    {
    
    
        vector<int> max_value;
        MyQueue myque;
        for (int i = 0; i < k; i++)  // 先将前k的元素放进队列
        {
    
    
            myque.push(nums[i]);
        }
        max_value.push_back(myque.front());
        for(int i=k;i<nums.size();i++)
        {
    
    
            myque.pop(nums[i-k]);
            myque.push(nums[i]);
            
            int max=myque.front();
            max_value.push_back(max);
        }
        return max_value;
    }
};

猜你喜欢

转载自blog.csdn.net/mabaizi/article/details/128737676