我们需要时刻理解栈和队列这两种数据结构的思想。

栈:先进后出,用于保存现场。

队列:先进先出,用于表示优先级。

不管是栈还是队列,在操作的时候需要时刻记住判空,否则top()和front()函数会报错。

1 用栈实现队列

2 用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

解题思路:

  • 1 数据结构:一个队列
  • 2 输入:每输入一个数,就将该数前面的全部元素移到末尾(pop最前一个数,push到末尾)。
  • 3 输出:输出最前方的数。

3 有效的括号

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

解题思路:

  • 1 数据结构:栈
  • 2 输入:
    • 遇到”({[“,就push进栈
    • 遇到”)}]”,如果栈为空,则return fasle
    • 遇到”)”就判断栈顶部元素是否为”(“,是则pop(),否则return false
    • 遇到”}”就判断栈顶部元素是否为”{“,是则pop(),否则return false
    • 遇到”]”就判断栈顶部元素是否为”[“,是则pop(),否则return false

代码如下:


class Solution {
public:
    bool isValid(string s) {
        stack st;
        for(auto c:s){
            if (c == '(' || c == '{' || c == '[') st.push(c);
            else{
                if (st.empty()) return false;
                if (c == ')' && st.top() != '(') return false;
                else if (c == '}' && st.top() != '{') return false;
                else if (c == ']' && st.top() != '[') return false;
                st.pop();
            }
        }
        return st.empty();
    }
};

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

给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

解题思路:

  • 1 数据结构:使用栈存储过去元素。
  • 2 输入:如果栈不为空,且当前元素与栈顶元素相同,则栈顶元素出栈,否则将该元素进栈。
  • 3 输出:栈中元素取反则为最终输出字符串。

代码如下:

class Solution {
public:
    string removeDuplicates(string s) {
        string result = "";
        stack st;
        st.push(s[0]);
        for(int i=1; i<s.size(); i++){
            if (!st.empty() && s[i] == st.top()) st.pop();
            else st.push(s[i]);
        }
        while(!st.empty()){
            result += st.top();
            st.pop(); 
        }
        reverse(result.begin(), result.end());
        return result;
    }
};

5 逆波兰表达式

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

解题思路:

  • 1 遇到数字就进栈
  • 2 遇到计算符号就取出栈顶2个元素进行计算,将计算结果进栈。

代码如下:

class Solution {
public:
    int evalRPN(vector& tokens) {
        stack st;
        for(auto t:tokens){
            if (t=="+" || t=="-" || t=="*" || t=="/"){
                int x = stoi(st.top()); st.pop();
                int y = stoi(st.top()); st.pop();
                int z;
                if (t=="+") { z = x + y; st.push(to_string(z));}
                else if (t=="-") {z = y - x; st.push(to_string(z));}
                else if (t=="*") {z = x * y; st.push(to_string(z));}
                else if (t=="/") {z = y / x; st.push(to_string(z));}
            }else st.push(t);
        }
        int result = stoi(st.top());
        return result;
    }
};

6 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 

暴力做法:超时

解题思路:从滑动窗口内找到最大值,存储到result数组中。

代码如下:

class Solution {
public:
    vector maxSlidingWindow(vector& nums, int k) {
        vector result;
        int m = nums.size();
        for(int i=0; i<m-k+1; i++){
            int maxValue = INT_MIN;
            for(int j=i; j<i+k; j++){
                maxValue = max(maxValue, nums[j]);
            }
            result.push_back(maxValue);
        }
        return result;
    }
};

单调队列:

解题思路:

  • 1 输入:
    • 当输入元素大于单调队列的首元素时,则将队列清空,push该元素到单调队列。
    • 当输入元素小于单调队列的首元素时,则push该元素到单调队列。
  • 2 输出:

7 前k个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

解题思路:

  • 1 统计元素的频率 – 字典
  • 2 对元素的频率进行排序 – vector排序/ 优先级队列
  • 3 返回前k个元素

vector排序:

class Solution {
public:
    vector topKFrequent(vector& nums, int k) {
        unordered_map<int, int> my_map;
        vector result;
        for(auto num:nums) my_map[num]++;
        vector<pair<int, int>> vec(my_map.begin(), my_map.end());
        sort(vec.begin(), vec.end(), [](pair<int, int> a, pair<int, int> b){return a.second > b.second;});
        for(int i=0; i<k; i++) result.push_back(vec[i].first);
        return result;
    }
};

优先级队列: