栈与队列

106 阅读5分钟

用栈模拟队列和用队列模拟栈

leetcode.232

  • 链接leetcode.cn/problems/im…
  • 解题方法:栈:先入后出,队列:先入先出
    取栈顶元素,取队头元素:top(),front()
    栈、队列判空:empty() 栈、队列添加元素:push() 栈、队列删除元素:pop()
  • leetcode解题代码
class MyQueue {
public:
    stack<int> a, b;
    MyQueue() {
    }
    
    void push(int x) {
        a.push(x);
    }
    
    int pop() {
        while (a.size() > 1) b.push(a.top()), a.pop();
        int cur = a.top();
        a.pop();
        while (b.size()) a.push(b.top()), b.pop();
        return cur;
    }
    
    int peek() {
        while (a.size() > 1) b.push(a.top()), a.pop();
        int cur = a.top();
        while (b.size()) a.push(b.top()), b.pop();
        return cur;
    }
    
    bool empty() {
        return a.empty();
    }
};

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

leetcode.225

  • 链接leetcode.cn/problems/im…
  • 解题方法:栈:先入后出,队列:先入先出
    取栈顶元素,取队头元素:top(),front()
    栈、队列判空:empty() 栈、队列添加元素:push() 栈、队列删除元素:pop()
  • leetcode解题代码
class MyStack {
public:
    queue<int> a, b;
    MyStack() {
    }
    
    void push(int x) {
        a.push(x);
    }
    
    int pop() {
        while (a.size() > 1) b.push(a.front()), a.pop();
        int cur = a.front();
        a.pop();
        while (b.size()) a.push(b.front()), b.pop();
        return cur;
    }
    
    int top() {
        while (a.size() > 1) b.push(a.front()), a.pop();
        int cur = a.front();
        a.pop();
        while (b.size()) a.push(b.front()), b.pop();
        a.push(cur);
        return cur;
    }
    
    bool empty() {
        return a.empty();
    }
};

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

用栈先入后出的性质解题

leetcode.20

  • 链接leetcode.cn/problems/va…
  • 解题方法1:先将所有的左括号入栈,注意入栈顺序要跟匹配顺序相同
    匹配右括号,如果栈为空或不能够匹配当前括号则返回false
  • leetcode解题代码
class Solution {
public:
    bool isValid(string s) {
        stack<char> stk;
        for (auto c: s){
            if (c == '(' || c == '[' || c == '{') stk.push(c);
            else if (c == ')'){
                if (stk.empty() || stk.top() != '(')
                    return false;
                stk.pop();
            }
            else if (c == ']'){
                if (stk.empty() || stk.top() != '[')
                    return false;
                stk.pop();
            }
            else if (c == '}'){
                if (stk.empty() || stk.top() != '{')
                    return false;
                stk.pop();
            }
        }
        return stk.empty();
    }
};
  • 解题方法2:先将所有的左括号入栈,注意入栈顺序要跟匹配顺序相同
    匹配右括号,可以查询ASKII码发现无论是大中小括号,其左右括号的ASKII码相差小于等于2
  • leetcode解题代码
class Solution {
public:
    bool isValid(string s) {
        stack<char> stk;
        for (auto c: s){
            if (c == '(' || c == '[' || c == '{') stk.push(c);
            else {
                if (stk.empty() || abs(c - stk.top()) > 2) return false;
                stk.pop();
            }
        }
        return stk.empty();
    }
};

leetcode.1047

  • 链接leetcode.cn/problems/re…
  • 解题方法:遍历所有字符,如果栈不空且栈顶元素与遍历的当前元素相同,删除栈顶元素,否则将当前元素加入栈中
    遍历栈加入答案数组,由于栈是先入后出所以需要反转一下答案数组
  • leetcode解题代码
class Solution {
public:
    string removeDuplicates(string s) {
        stack<char> stk;
        string res;
        for (auto c: s){
            if (stk.size() && stk.top() == c) stk.pop();
            else stk.push(c);
        }

        while (stk.size()){
            res += stk.top();
            stk.pop();
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

leetcode.150

  • 链接leetcode.cn/problems/ev…
  • 解题方法:遍历所有字符,如果字符为运算符则将当前栈顶的两个元素弹出做相应计算(注意:弹出是先弹出后面的元素,减法和除法需要注意元素计算顺序)
    如果字符为数字,则入栈(注意转换格式)
  • leetcode解题代码
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> stk;
        for (auto c: tokens){
            if (c == "+" || c == "-" || c == "*" || c == "/"){
                long b = stk.top(); stk.pop();
                long a = stk.top(); stk.pop();
                if (c == "+") stk.push(a + b);
                else if (c == "-") stk.push(a - b);
                else if (c == "*") stk.push(a * b);
                else if (c == "/") stk.push(a / b);
            }
            else stk.push(stoi(c));
        }
        return stk.top();
    }
};

单调栈

  • 常见模型:找出每个数左边/右边离它最近的比它大/小的数
  • 单调栈模板
for (int i = 0; i < n; i ++ )// 根据题目调整遍历顺序
{
    while (stk.size() && check(stk[tt], i)){
        // 
    }
    stk.push();
}

leetcode.496

  • 链接leetcode.cn/problems/ne…
  • 解题方法:在nums2中用单调栈模板找出所有当前数下一个更大的元素用数组q存储
    用哈希表存储nums2元素对应下标
    在nums1中利用哈希表和数组q找对应的下一个更大元素
  • leetcode解题代码
class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        stack<int> stk;
        vector<int> q(nums2.size());
        // 若当前元素大于栈顶元素,记录并删除栈顶元素,将当前元素入栈
        for (int i = 0; i < nums2.size(); i ++){
            int x = nums2[i];
            while (stk.size() && x > nums2[stk.top()]){
                int t = stk.top();
                stk.pop();
                q[t] = nums2[i];
            }
            stk.push(i);
        }
        // 大于栈顶元素的记录过了,小于的没入栈剩下的都是等于的,将其赋值为-1
        while (stk.size()){
            q[stk.top()] = -1;
            stk.pop();
        }

        unordered_map<int, int> hash;
        for (int i = 0; i < nums2.size(); i ++) hash[nums2[i]] = i;

        vector<int> res;
        for (auto c: nums1){
            res.push_back(q[hash[c]]);
        }
        return res;
    }
};

leetcode.503

  • 链接leetcode.cn/problems/ne…
  • 解题方法:解决环形问题,可以将数组复制一份接到原数组之后
    同样用单调栈模板解题
  • leetcode解题代码
class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int n = nums.size();
        nums.insert(nums.end(), nums.begin(), nums.end());
        stack<int> stk;
        vector<int> res(2 * n);
        for (int i = 0; i < 2 * n; i ++){
            int x = nums[i];
            while (stk.size() && x > nums[stk.top()]){
                int t = stk.top();
                stk.pop();
                res[t] = nums[i];
            }
            stk.push(i);
        }

        while (stk.size()){
            res[stk.top()] = -1;
            stk.pop();
        }

        res.resize(n);
        return res;

    }
};

leetcode.739

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

单调队列

  • 常见模型:找出滑动窗口中的最大最小值
  • 如果求滑动窗口的最大值那么即为一个递减序列(队头为最大值)
  • 如果求滑动窗口的最小值那么即为一个递增序列(队头为最小值)
  • 单调队列模板
for (int i = 0; i < n; i ++ )
{
    // 合法性判断(是否超出滑动窗口的大小)
    while (q.size() && check()) q.pop_front();  // 判断队头是否滑出窗口
    // 单调性判断(添加的元素是否符合队列单调性)
    while (q.size() && check()) q.pop_back();
    // 在队尾添加元素
    q.push_back(i);
    // 能够形成滑动窗口了开始维护答案
    if (i >= k - 1) res.push_back();
}

leetcode.239

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> q;
        vector<int> res;
        for (int i = 0; i < nums.size(); i ++){
            while (q.size() && i - q.front() + 1 > k) q.pop_front();
            while (q.size() && nums[i] >= nums[q.back()]) q.pop_back();
            q.push_back(i);
            if (i >= k - 1) res.push_back(nums[q.front()]);
        }
        return res;
    }
};

优先队列

  • 优先队列是一种会按照默认或自定义的优先级进行自动排序的队列,其特点是优先级高的元素排在队首,低的排在队尾,可以理解为维护一个堆
  • 头文件#include <queue>中提供了两种可直接引用的优先规则(排序规则):greater、less;其中,less是默认优先规则,大根堆,可以理解为从堆顶元素向下逐渐减小;greater表示小根堆,堆顶元素向下逐渐变大

leetcode.215

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // 优先队列,建小根堆即堆顶元素为最小值,这样更新的时候可以删除堆顶元素
        priority_queue<int, vector<int>, greater<int>> heap;
        // 直接入堆,如果大于k则删除元素(删除的都是最小值)
        for (auto c: nums){
            heap.push(c);
            if (heap.size() > k){
                heap.pop();
            }
        }
        // 输出
        return heap.top();
    }
};

leetcode.347

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        // 用哈希表记录每个数字出现的次数
        unordered_map<int, int> hash;
        for (auto c: nums) hash[c] ++;
        // 重载优先队列排序,pair<数字,出现的次数>,以数字出现的次数排序
        struct mycompare{
            bool operator () (const pair<int, int> &a, const pair<int, int> &b){
            return a.second > b.second;
            }
        };
        // 定义优先队列
        priority_queue<pair<int, int>, vector<pair<int, int>>, mycompare> heap;
        // 将哈希表入队,如果元素个数超过k则删除堆顶元素(最小值)
        for (auto x: hash){
            heap.push(x);
            if (heap.size() > k){
                heap.pop();
            }
        }
        // 输出结果,输出的是pair中的数字
        vector<int> res;
        while (heap.size()){
            res.push_back(heap.top().first);
            heap.pop();
        }
        return res;
    }
};