LeetCode队列和栈专项练习(题解)

LeetCode练习在这里

栈和队列

初识队列

设计循环队列

class MyCircularQueue {
private:
    int head;
    int tail;
    int capacity;
    vector<int> a;
public:
    MyCircularQueue(int k) {
        head = -1;
        tail = -1;
        capacity = k;
        a.resize(k);
    }
    
    bool enQueue(int value) {
        if (isFull())
            return false;
        if (isEmpty())// 特判如果队列为空的时候,想让head初始化
            head = 0;
        tail = (tail + 1) % capacity;
        a[tail] = value;
        return true;
    }
    
    bool deQueue() {
        if (isEmpty())
            return false;
        if (head == tail)// 如果head==tail还要删除元素,说明删完元素后队列为空,
        {                //  就直接让队列回到一开始的位置上
            head = -1;
            tail = -1;
            return true;
        }
        head = (head + 1) % capacity;// 区其余的情况直接让head往后移一位
        return true;
    }
    
    int Front() {
        if (isEmpty())
            return -1;
        return a[head];// 若队列不空则取出队头
    }
    
    int Rear() {
        if (isEmpty())
            return -1;
        return a[tail];// 如果队列不空则取出队尾
    }
    
    bool isEmpty() {
        return head == -1;// 如果规定了head为-1,队列为空
    }
    
    bool isFull() {
        return (tail + 1) % capacity == head;// 如果tail的下一个是head说明队列已满
    }
};

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue* obj = new MyCircularQueue(k);
 * bool param_1 = obj->enQueue(value);
 * bool param_2 = obj->deQueue();
 * int param_3 = obj->Front();
 * int param_4 = obj->Rear();
 * bool param_5 = obj->isEmpty();
 * bool param_6 = obj->isFull();
 */

如果head和tail一开始不设置为-1的话,就无法判断何时空何时满。因为这样的话,当head==tail的时候,可能是空也可能是满,这样就会引起歧义

队列和BFS

BFS模板

int BFS(Node root, Node target) {
    Queue<Node> queue;  // store all nodes which are waiting to be processed
    int step = 0;       // number of steps neeeded from root to current node
    // initialize
    add root to queue;
    // BFS
    while (queue is not empty) {
        step = step + 1;// 路径长度+1
        // iterate the nodes which are already in the queue
        int size = queue.size();
        for (int i = 0; i < size; ++i) {
            Node cur = the first node in queue;
            return step if cur is target;
            for (Node next : the neighbors of cur) {
                add next to queue;
            }
            remove the first node from queue;
        }
    }
    return -1;          // there is no path from root to target
}

*岛屿数量(模板题)

(求连通块数量)

*BFS(宽搜的模板)

宽搜的模板

1)将要处理的结点全部放在队列中

2)只要队列不空就一直扩展队列(将左右要处理的结点全部放在队列中)

3)中间可以在取出队列中的元素的时候,特判一下是不是答案

class Solution {
public:
    typedef pair<int, int> PII;
    void bfs(pair<int, int> point, vector<vector<char>>& grid) {
        int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
        int n = grid.size(), m = grid[0].size();
        queue<PII> q;
        set<PII> st;
        // 将起始位置放入队列当中
        q.push(point);
        st.insert(point);

        while (!q.empty()) {
            PII tmp = q.front();
            q.pop();
            grid[tmp.first][tmp.second] = '0';// 将队列的头结点置零

            // 扩展队头
            for (int i = 0; i < 4; i ++) {
                int x = dx[i] + tmp.first, y = dy[i] + tmp.second;
                if (x >= 0 && x < n && y >= 0 && y < m && 
                grid[x][y] == '1' && !st.count({x, y})) {
                    q.push({x, y});
                    st.insert({x, y});
                }
            }
        }
    }

    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size(), m = grid[0].size();
        int ans = 0;
        for (int i = 0; i < n; i ++) {
            for (int j = 0; j < m; j ++)
                if (grid[i][j] == '1') {
                    ans ++;
                    bfs({i, j}, grid);
                }
        }

        return ans;
    }
};
*DFS(深搜的模板)

递归的时候三步走

1)处理递归何时退出

2)处理当前位置结点的情况

3)处理剩下的结点的情况

class Solution {
public:
	int n = grid.size(), m = grid[0].size();
	int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
    void vis(vector<vector<char>>& grid, int i, int j) {
        // 处理退出的情况
        if (grid[i][j] == '0') {// 遇到0说明已经到头了,就可以返回了
            return ;
        }
        // 处理当前节点的情况
        grid[i][j] = '0';// 将当前位置置零
        
        // 处理剩下结点的情况
        for (int k = 0; k < 4; k ++ ) {
            int x = dx[k] + i, y = dy[k] + j;// 枚举四个方向然后扩展
            if (x >= 0 && x < n && y >= 0 && y < m) {
                vis(grid, x, y);
            }
        }
    }


    int numIslands(vector<vector<char>>& grid) {
        int ans = 0;
        for (int i = 0; i < n; i ++) {
            for (int j = 0; j < m; j ++) {
                if (grid[i][j] == '1') {// 如果遇到的是1就说明要扩展开来
                    vis(grid, i, j);
                    ans ++;
                }
            }
        }
        return ans;
    }
};

*打开转盘锁(模板题)

(暴搜或者枚举每一位上的数字)

当我们需要计算BFS的最短距离的时候,就要算出一个点(起始点)往外的层数,这时候就又要在添加变量depth来记录一下当前的层数,可以用一个循环while (size - -)来控制,这样当size走完,就是一层的结点都消耗完了。

一个set

class Solution {
public:
    string upStr(string& str, int i)
    {
        string tmp = str;
        tmp[i] = tmp[i] == '9' ? '0' : tmp[i] + 1;
        return tmp;
    }
    string downStr(string& str, int i)
    {
        string tmp = str;
        tmp[i] = tmp[i] == '0' ? '9' : tmp[i] - 1;
        return tmp;
    }

    int openLock(vector<string>& deadends, string target) {
        set<string> vis(deadends.begin(), deadends.end());// 访问过的字符串和不能走的字符串
        queue<string> q;// 队列中的字符串
        if (vis.count("0000"))
            return -1;
        q.push("0000");
        vis.insert("0000");

        int depth = 0;
        while (!q.empty())// ***搜索符合条件所有的结点
        {
            int size = q.size();// 这一层的所有结点全部入队
            while (size -- )// ***搜索这一层的结点
            {
                string cur = q.front();
                q.pop();
                if (cur == target)// 如果已经找到答案就直接返回答案
                    return depth;
                for (int i = 0; i < 4; i ++)
                {
                    string subA = upStr(cur, i);// 向上转动一个
                    string subB = downStr(cur, i); // 向下转动一个
                    if (!vis.count(subA))
                    {
                        vis.insert(subA);
                        q.push(subA);
                    }
                    if (!vis.count(subB))
                    {
                        vis.insert(subB);
                        q.push(subB);
                    }
                }
            }
            depth ++;
        }
        return -1;
    }
};
两个set
class Solution {
public:
    string upStr(string& str, int i)
    {
        string tmp = str;
        tmp[i] = tmp[i] == '9' ? '0' : tmp[i] + 1;
        return tmp;
    }
    string downStr(string& str, int i)
    {
        string tmp = str;
        tmp[i] = tmp[i] == '0' ? '9' : tmp[i] - 1;
        return tmp;
    }

    int openLock(vector<string>& deadends, string target) {
        set<string> vis;// 访问过的字符串
        set<string> dead(deadends.begin(), deadends.end());// 不能走的字符串
        queue<string> q;// 队列中的字符串
        q.push("0000");
        vis.insert("0000");

        int depth = 0;
        while (!q.empty())
        {
            int size = q.size();// 这一层的所有结点全部入队
            while (size --)
            {
                string cur = q.front();
                q.pop();
                if (cur == target)// 如果已经找到答案就直接返回答案
                    return depth;
                if (dead.count(cur))// 如果cur在dead中就不能访问
                    continue;
                for (int i = 0; i < 4; i ++)
                {
                    string subA = upStr(cur, i);// 向上转动一个
                    string subB = downStr(cur, i); // 向下转动一个
                    if (!vis.count(subA))
                    {
                        vis.insert(subA);
                        q.push(subA);
                    }
                    if (!vis.count(subB))
                    {
                        vis.insert(subB);
                        q.push(subB);
                    }
                }
            }
            depth ++;
        }
        return -1;
    }
};

以上两种写法区别不大

*完全平方数

BFS
class Solution {
public:
    int numSquares(int n) {
        queue<int> q;
        set<int> vis;
        q.push(0);
        vis.insert(0);
        int depth = 1;
        
        while (!q.empty())
        {
            int size = q.size();// 计算出这一层的结点有多少个
            while (size --)
            {
                int tmp = q.front();
                q.pop();
                for (int i = 1; i <= n / i; i ++)// 计算平方数然后累加到tmp上
                {
                    int val = tmp + i * i;
                    if (val == n)// 如果等于目标值就返回当前结点的所在的层数
                        return depth;
                    else if (val > n)// 如果大于目标值就可以直接退出了
                        break;       // 因为后面的值一定不可能再等于目标值了
                    else if (!vis.count(val))// 如果小于目标值并且没有在队列中,就加入队列
                    {
                        vis.insert(val);
                        q.push(val);
                    }
                }
            }
            depth ++;
        }
        return depth;
    }
};

简单分析思路:这题是求出最少的步数问题再看有还是让我们枚举所有的情况,所以自然的想到使用BFS来解决,而且这道题目是要算出最少的步数所以可以用depth来算出层数(就是最小值)。那如何利用队列来枚举所有的数呢?我们其实就可以每次把不超过n的平方数放到队列中就可以了,中甲只要用for循环来枚举一下平方数再判断一下就可以了,其中可以分为3中情况1)平方数和原来的数相加后很好等于答案就可以返回答案了2)平方数和原来的数相加后比答案要小,说明他还有潜力成为答案所以就放到队列中等待下一次的取出在和平方数相加3)如果相加完后比答案要大了,说明这个平方数永远不能整好凑出答案了就可以直接结束循环了

DP
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1);
        dp[0] = 0;
        for (int i = 1; i <= n; i ++)
        {
            dp[i] = i;// 最坏的情况就是数字i都是由1组成的,所以有i个1组成i
            for (int j = 1; j * j <= i; j ++)
                dp[i] = min(dp[i], dp[i - j * j] + 1);            
        }

        return dp[n];
    }
};

dp在这章不做重点可以先跳过,可以看dp专题后再到那里理解

最小栈

双栈(多开一个保留最小值的栈)
class MinStack {
    stack<int> sk1;// 放全部的元素
    stack<int> sk2;// 只放最小元素
public:
    /** initialize your data structure here. */
    MinStack() {

    }
    
    void push(int val) {
        sk1.push(val);
        // 只有当最小栈为空或者插入的元素比最小栈的栈顶元素还要小的时候才入栈,这是栈顶的元素还是最小的元素
        if (sk2.empty() || getMin() >= val)
            sk2.push(val);
    }
    
    void pop() {
        int Pop = sk1.top();
        sk1.pop();
        if (Pop == getMin())// 如果出栈的元素是当前的最小元素,那sk2也要更新
            sk2.pop();
    }
    
    int top() {
        return sk1.top();
    }
    
    int getMin() {
        return sk2.top();
    }
};

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

这道题其实思路不难,就是利用两个栈,一个栈是专门放当前的最小元素的(即最小栈的栈顶元素就是当前的最小元素),如果当pop的时候,将最小栈中的元素pop掉的话,就让最小栈中的元素也pop就可以了

单栈(在栈中保留上一次的最小值)
class MinStack {
    int Min = INT_MAX;
    stack<int> sk;
public:
    /** initialize your data structure here. */
    MinStack() {

    }
    
    void push(int val) {
        if (val <= Min)
        {
            sk.push(Min);// 保留上一次的最小值
            Min = val;// 更新当前的最小值
        }
        sk.push(val);
    }
    
    void pop() {
        int Pop = sk.top();
        sk.pop();
        if (Pop == Min)
        {
            Min = sk.top();// 将当前最小值的上一个保留的最小值取出
            sk.pop();            
        }

    }
    
    int top() {
        return sk.top();
    }
    
    int getMin() {
        return Min;
    }
};

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

为了节省掉stack的空间和容器本身调函数的时间,所以就需要用一个变量(Min)来记录当前的最小值,但是有一个问题,就是如果栈中将当前的最小值Min弹出后,那这时候的最小值是什么呢?这就需要我们记录一下每次的最小值,就是每次在更新最小值的时候,将上次的最小值放到栈中,这样当pop到最小值的时候就可以将最小值下面的元素取出(也就是上次的最小值),更新Min就可以了

有效括号

class Solution {
public:
    bool isValid(string s) {
        stack<char> sk;
        for (auto ch : s)
        {
            if (ch == '(')
                sk.push(')');
            else if (ch == '[')
                sk.push(']');
            else if (ch == '{')
                sk.push('}');
            else if (sk.empty())
                return false;
            else
            {
                char t = sk.top();
                if (t != ch)
                    return false;
                sk.pop();
            }
        }
        return sk.empty();
    }
};

大部分的括号匹配问题都可以用栈来解决,这题也是一样(因为符合先进后出的原则)。可以确定是如果没有出现左括号就要右括号了,说明就一定不能配对了,因为有左括号还有右括号可以抵消,但是右括号在前就没有左括号可以和它抵消了。所以利用这一点,就可以用用栈来保存当前位置应该匹配的括号是什么,比如说当遇到(,就可以将)入栈,这是因为如果后面有对应位置的)和他匹配的话,就会消掉。这样就可以从外往里进左括号,总里面往外消掉右括号。其中有一个要特判的就是,如果栈空了就说明还没有左括号就有右括号了,这时候就可以直接返回false了,如果括号不匹配也返回false。

逆波兰表达式

class Solution {
public:
    void choice(string& ch, stack<string>& sk) {
        int b = stoi(sk.top());
        sk.pop();
        int a = stoi(sk.top());
        sk.pop();
        int num = 0;
        if (ch == "+") num = a + b;
        if (ch == "-") num = a - b;
        if (ch == "*") num = a * b;
        if (ch == "/") num = a / b;
        sk.push(to_string(num));
    }

    int evalRPN(vector<string>& tokens) {
        stack<string> sk;
        int n = tokens.size();
        for (int i = 0; i < n; i ++) {
            auto ch = tokens[i];
            if (ch == "*" || ch == "/" || ch == "+" || ch == "-") 
                choice(ch, sk);
            else 
                sk.push(ch);
        }
        int ans = stoi(sk.top());
        return ans;
    }
};

经典单调栈

*每日温度(单调栈模板题)

暴力解法(不能AC)
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& t) {
        vector<int> ans(t.size(), 0);
        for (int i = 0; i < t.size(); i ++)
        {
            for (int j = i + 1; j < t.size(); j ++)// 遍历i数字后面的数,知道遇到第一个比a[i]大的数停下
            {
                if (t[j] > t[i])
                {
                    ans[i] = j - i;
                    break;                    
                }
            }
        }
        return ans;
    }
};

每次都从左往右找,但是这样的效率太低了,O(N2)的时间复杂度,能不能用O(N)的时间也就是只扫描一遍数组就可以了呢?这就需要单调栈了

单调栈问题一般解决的就是找左边和右边的离当前元素最近的最大值(最小值)

单调栈模板

stack<int> sk;
for (int i = 0; i < arr.size(); i ++)// 循环数组
{
    while (!sk.empty() && /* 比较大小 */ ) // 维护一个单调栈
    {
        // 可进行操作,可求出右边最近的最值
        sk.pop();
    }
    // 可进行操作,可求出左边最近的最值
    sk.push(i);
}

维护递减的栈就是找最大的元素

维护递增的栈就是找最小的元素

单调栈解法

找到右边最近的比当前元素大的元素

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& t) {
        vector<int> ans(t.size(), 0);
        stack<int> sk;
        for (int i = 0; i < t.size(); i ++)
        {
            while (!sk.empty() && t[i] > t[sk.top()])// 维护一个单调栈
            {
                ans[sk.top()] = i - sk.top();// 在弹出的时候计算,因为a[i]元素然你弹出,说明他是离你最近的比你大的右侧元素
                sk.pop();
            }
            sk.push(i);
        }
        return ans;
    }
};

先用这题来讲一下单调栈的思路:比如说这一道题,要求出右边的最大值,所以要维护一个单调递减的栈(后面会解释为什么要维护递减的单调栈),可以想一下如果要找到右边最大值,又因为是从左往右遍历,所以如果都将元素放入栈中的话,如果即将插入的元素比栈顶元素要大(即上次插入的元素),说明这个元素就是栈顶元素的最右边的最大的元素(最近是因为是从左往右遍历数组)。这时就可以让栈顶元素弹出了,如果弹出后的栈顶元素还比当前要插入的元素小,说明此时的栈顶元素的最右边的最近的最大值还是当前待插入的元素。就这样类推下去直到找到一个元素比这个元素要大就停止。所以说每次只要有大的元素进入栈中的小元素就要退出,所以说栈中只有递减的序列这样才能不让元素退出。答案就是每当有元素退出的时候,让他退出的元素也就是比他大的最近的元素,在元素退出之前记录一下栈顶元素的最近的最大值的下标即可。可以画图体会一下

数组模拟栈解法
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& t) {
        vector<int> ans(t.size(), 0);
        int top = -1;
        int a[t.size()];
        for (int i = 0; i < t.size(); i ++)
        {
            while (top >= 0 && t[i] > t[a[top]])// 用数组维护一个单调栈
            {
                int idx = a[top--];
                ans[idx] = i - idx;
            }
            a[++top] = i;
        }
        return ans;
    }
};

*[柱状图中最大矩形(单调栈模板题)](84. 柱状图中最大的矩形 - 力扣(LeetCode) (leetcode-cn.com))

暴力解法(不能AC,会TLE)

枚举高度,枚举宽度也可以

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        int ans = 0;
        for (int i = 0; i < n; i ++ ) {
            int mid = heights[i];
            int right = i, left = i;
            while (left >= 0 && heights[left] >= mid)
                left --;
            while (right < n && heights[right] >= mid)
                right ++;
                
            ans = max(ans, (right - left - 1) * heights[i]);
        }
        return ans;
    }
};

其实这题一下子看不出来时在考单调栈的问题,但是可以想象一下,如果固定一个矩形的高度,一个矩形最大可以延伸到的地方就是要求出的矩形的最大宽度。如何找到延伸的最大地方,其实就是在找一个矩形的左右两端离他最近的两个最小的矩形在哪里,因为如果另一个矩形的高度比当前的矩形更高,其实照样可以以当前的矩形的高度作高,但是如果另一个矩形的高度小于当前的额矩形的高度,这时候其实就不能以当前的矩形的高度作高形成的矩形了,所以这道题目就转化成为找一个元素左右最近的最小值所以可以用单调栈来解决

因为开头的元素左边没有最小值,结尾的元素右边没有最小值,所以要特判一下是不是边界,如果是设置为-1或者数组的大小

O(N)解法
class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        stack<int> mono_sk;// 单调栈
        int Left[n], Right[n];
        for (int i = 0; i < n; i ++) {
            while (!mono_sk.empty() && heights[mono_sk.top()] >= heights[i]) {
                mono_sk.pop();
            }
            Left[i] = mono_sk.empty() ? -1 : mono_sk.top();// 离你最近的左侧的最小的元素
            mono_sk.push(i);
        }

        // 下面的过程就是从后往前循环,求出反过来的元素的最左边的最小值,也就是从右往左看的右侧的最小值
        mono_sk = stack<int>();
        for (int i = n - 1; i >= 0; i --) {
            while (!mono_sk.empty() && heights[mono_sk.top()] >= heights[i]) {
                mono_sk.pop();
            }
            Right[i] = mono_sk.empty() ? n : mono_sk.top();
            mono_sk.push(i);
        }

        int ans = 0;
        for (int i = 0; i < n; i ++ ) {
            ans = max(ans, (Right[i] - Left[i] - 1) * heights[i]);
        }

        return ans;

    }
};

每日温度拿到题目是哪个元素让你出去哪个元素就是你的右边最近的最小值,这道题目也可以这么做,但是我换了一种写法,就是如果元素全部退出后,大的那个元素还没有插入之前,这时栈顶的元素没有退出说明栈顶元素比插入元素小说明当前的遍历到的元素的最左边的最小值就是栈顶元素,如果并没有元素退出,那说明栈顶元素并当前的元素小,所以栈顶元素还是要比当前遍历到的元素要小。

*O(N)优化常数(单调栈的诠释)

哪个元素让你出去,哪个元素就是离你最近的最小值

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        stack<int> mono_sk;// 单调栈
        int Left[n], Right[n];// 将right数组中的数全部初始化为n
        fill(Right, Right + n, n);
        for (int i = 0; i < n; i ++) {
            while (!mono_sk.empty() && heights[mono_sk.top()] >= heights[i]) {
                Right[mono_sk.top()] = i;
                mono_sk.pop();
            }
            Left[i] = mono_sk.empty() ? -1 : mono_sk.top();
            mono_sk.push(i);
        }

        int ans = 0;
        for (int i = 0; i < n; i ++ ) {
            ans = max(ans, (Right[i] - Left[i] - 1) * heights[i]);
        }
        
        return ans;
    }
};

遍历一遍当然值能求出一端的值,要么左侧要么右侧,但是如果只循环一遍,结合上面的题目是可以做到的,但是这样可能还有元素在栈中没有全部遍历完,但是其实栈中的元素只是右边没有确定而已,但是右边又没有更小的元素了,所以右边界就都是n(数组的元素个数),所以开始的时候就将所有的元素都设为n就可以了

用单调栈模板的两个问题:

1)当循环过一遍后,每个元素都可以得到最值吗?

如果是找右边界,是很有可能找不到的,因为可能最后栈中还是有单调性的但是还是有剩余的元素,所以如果需要找有边界我们就需要在数组元素的初始化上做一些改动,比如说每日温度那题如果最后没有找到右边有更大的元素的时候就说明这些元素没有离它们最近的大的元素所以就就是0,那初始化的时候就将元素全部初始化为0就可以了。柱形图中的最大矩阵的右边界如果没有右边的最近的最小元素,那它们的右边界就是数组的大小,所有初始化的时候就可以初始化为数组的大小。fill(arr, arr + arr.size(), 0) vector<int> arr(Size, 0)两个种初始化的方式,一种用函数,一种用容器的特性

2)判断左边最值和右边最值的方式怎么区别?为什么?

左边界是每次都要判断一次,右边界时每次弹出栈中的元素的时候才判断

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yceFFf2I-1621814451908)(C:\Users\张昊宇\AppData\Roaming\Typora\typora-user-images\image-20210520142335308.png)]

栈和DFS

DFS的递归模板

void dfs(args...)
{
    // 递归退出的条件
    
    // 当前结点的处理
    
    // 剩余结点的处理
}

目标和3

后序遍历
class Solution {
public:
    int dfs(vector<int>& nums, int target, int pos) {
        if (pos == nums.size() && target == 0) {
            return 1;
        }
        if (pos >= nums.size()) 
            return 0;

        int ans = 0;
        ans += dfs(nums, target + nums[pos], pos + 1);// 加上当前的数
        ans += dfs(nums, target - nums[pos], pos + 1);// 减去当前的数
        
        return ans;
    }
    int findTargetSumWays(vector<int>& nums, int target) {
        return dfs(nums, target, 0);
    }
};
$动态规划

DFS的非递归(栈的显示调用)

void dfs(T root, T target) {
    set<int> vis;
    stack<int> sk;
    sk.push(root);
    
    while (!sk.empty())
    {
        auto tmp = sk.top();// 取出栈顶元素
        if (tmp == target)// 判断题目条件
            return ....;
        for (auto next : tmp)// 扩展栈的元素
        {
            if (!vis.count(next))// 判断是否已经如果栈
            {
                sk.push(next);
                vis.count(vis);
            }
        }
    }
}

二叉树的中序遍历

递归版
class Solution {
public:
    void _inorder(TreeNode* root, vector<int>& ans) {
        if (root == nullptr)
            return ;
        _inorder(root->left, ans);
        ans.push_back(root->val);
        _inorder(root->right, ans);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ans;
        _inorder(root, ans);
        return ans;
    }
};
非递归版
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<TreeNode*> sk;
        while (root != nullptr || !sk.empty()) {
            // 一直找树的最左端
            while (root != nullptr) {
                sk.push(root);
                root = root->left;
            }
            // 取出根节点的数
            root = sk.top();
            sk.pop();
            ans.push_back(root->val);
            // 将root移动到右树上
            root = root->right;
        }
        return ans;
    }
};

总结(栈和队列练习题)

用栈实现队列

class MyQueue {
private:
    stack<int> skIn;// 输入栈
    stack<int> skOut;// 输出栈
public:
    /** Initialize your data structure here. */
    MyQueue() {

    }
    
    /** Push element x to the back of queue. */
    void push(int x) {
        skIn.push(x);// 将元素放到输入栈中
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        // 如果在输出栈空的情况下才会将输入栈中的东西放入输出栈中
        // 如果输出栈中有元素,那就直接输出即可,否则就会让后面的元素覆盖到原来元素的位置
        if (skOut.empty()) {
            while (!skIn.empty()) {
                int t = skIn.top();
                skIn.pop();
                skOut.push(t);
            }            
        }

        int t = skOut.top();
        skOut.pop();
        return t;
    }
    
    /** Get the front element. */
    int peek() {
        int t = pop();// 直接复用pop函数的接口,将队列的头取出,就是队头元素然后再放回去
        skOut.push(t);
        return t;
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {
        return skIn.empty() && skOut.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();
 */

用队列实现栈

接口的复用很重要

class MyStack {
private:
    queue<int> q;
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 size = q.size();// 计算栈中元素的个数
        while (size > 1) {// 将队列通过其他元素尾插,让最后一个元素的相对位置变成队头元素
            int t = q.front();
            q.pop();
            q.push(t);
            size --;
        }
        int t = q.front();// 取出队头(栈顶元素),然后弹出即可
        q.pop();
        return t;
    }
    
    /** Get the top element. */
    int top() {
        int t = pop();// 复用pop函数的接口将栈顶的元素取出然后在放回栈中
        q.push(t);
        return t;
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return q.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();
 */

字符串解码(栈)

class Solution {
public:
    string decodeString(string s) {
        stack<int> nums;
        stack<char> strs;
        string ans;
        for (int i = 0; i < s.size(); i ++) {
            if (isdigit(s[i])) {
                int n = 0;
                int j = i; 
                while (j < s.size() && isdigit(s[j])) {// 抠出数字
                    n = n * 10 + (s[j] - '0');
                    j ++;
                }
                i = j - 1;
                nums.push(n);// 将数字放到栈中
            }
            else if (s[i] == ']')
            {
                string tmp;
                while (strs.top() != '[') {// 将括号中字母放到tmp中
                    tmp = strs.top() + tmp;
                    strs.pop();
                }
                strs.pop();// 将[弹出
                int n = nums.top();// 取出对应的个数
                nums.pop();
                while (n --) {// 放n次
                    for (int i = 0; i < tmp.size(); i++) {// 将tmp分解成字符放入栈中
                        strs.push(tmp[i]);
                    }
                    if (nums.size() == 0) // 如果到了最后一个括号就可以将字符串加到答案中了
                        ans += tmp;
                }
            }
            else if (nums.size() == 0)// 如果没有括号了局直接将字符加到字符串后面
                ans = ans + s[i];
            else
                strs.push(s[i]);
        }

        return ans;
    }
};
class Solution {
public:
    string decodeString(string s) {
        string ans;
        // 采用了两个栈,一个数字栈,一个字符栈
        stack<string> strs;
        stack<int> nums;

        int num = 0;

        for(int i = 0; i < s.size(); i++){
            // 判断是否为数字 是的话压入栈
            if(s[i] >= '0' && s[i] <= '9'){
                num = num*10 + (s[i] - '0');
            }

            // 不断的收集s中的字母
            else if(s[i] >= 'a' && s[i] <= 'z'){
                ans = ans + s[i];
            }

            // 遇到[字符,将收集到的数字压入栈,同时将数字置为0
            // (题目说了K为正整数,即一定存在 不可以为[abc]这样的形式)
            // 将之前收集到字符串压入字符栈
            else if(s[i] == '['){
                nums.push(num);
                num = 0;
                strs.push(ans);
                ans = "";
            }
            else if(s[i] == ']'){
                int number = nums.top();
                nums.pop();
                string tem = ans;
                for(int i = 0; i < number - 1; i++){
                    ans += tem;
                }

                string newStr = strs.top();
                strs.pop();
                ans = newStr + ans;
            }
            
        }

        return ans;

    }
};

图像渲染(连通块)

class Solution {
public:
    typedef pair<int, int> PII;
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
        // BFS的准备工作做好,queue和set
        queue<PII> q;
        set<PII> vis;
        int n = image.size(), m = image[0].size();
        q.push({sr, sc});
        vis.insert({sr, sc});
        int dis = image[sr][sc];// 目标值
        int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};

        while (!q.empty()) {
            auto tmp = q.front();
            q.pop();
            image[tmp.first][tmp.second] = newColor;// 将符合条件的地方都换上newColor
            for (int i = 0; i < 4; i ++) {
                int x = dx[i] + tmp.first, y = dy[i] + tmp.second;
                if (x >= 0 && x < n && y >= 0 && y < m 
                && image[x][y] == dis && !vis.count({x, y})) {
                    vis.insert({x, y});
                    q.push({x, y});
                }
            }
        }

        return image;
    }
};

01矩阵

class Solution {
public:
    typedef pair<int, int> PII;
    
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    vector<vector<int>>& bfs(vector<vector<int>>& mat, int i, int j) {
        int n = mat.size(), m = mat[0].size();
        int step = 0;// 开始的时候步数为0
        // 准备工作
        queue<PII> q;
        set<PII> vis;
        q.push({i, j});
        vis.insert({i, j});

        while (!q.empty()) {

            int size = q.size();// 一层一层结算
            while (size -- ){
            
            auto tmp = q.front();
            q.pop();

            if (mat[tmp.first][tmp.second] == 0) {
                mat[i][j] = step;
                return mat;
            }

            for (int k = 0; k < 4; k ++) {// 扩展四个方向
                int x = dx[k] + tmp.first, y = dy[k] + tmp.second;
                // 只要没有越界或者没有访问过就都可以入队
                if (x >= 0 && x < n && y >= 0 && y < m && !vis.count({x, y})) {
                    q.push({x, y});
                    vis.insert({x, y});
                }
            }
            }
            step ++;
        }
        return mat;
    }


    vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
        int n = mat.size(), m = mat[0].size();
        for (int i = 0; i < n; i ++) {
            for (int j = 0; j < m; j ++) {
                if (mat[i][j] == 1)
                    mat = bfs(mat, i, j);// 求出步数然后返回
            }
        }
        return mat;
    }
};

钥匙和房间(BFS和DFS)

BFS
class Solution {
public:
    bool canVisitAllRooms(vector<vector<int>>& rooms) {
        queue<int> q;
        set<int> vis;
        q.push(0);
        vis.insert(0);
        while (!q.empty()) {
            int cur = q.front();
            q.pop();
            int n = rooms[cur].size();// 求出cur这一行的元素个数
            for (int i = 0; i < n; i ++) {
                if (!vis.count(rooms[cur][i])) {
                    q.push(rooms[cur][i]);    
                    vis.insert(rooms[cur][i]);             
                }

            }
        }
        int sz = rooms.size();
        for (int i = 0; i < sz; i ++) {// 如果vis中没有0~sz中的所有数,说明就没有到过所有的房间
            if (!vis.count(i))
                return false;
        }
        return true;
    }
};
DFS
class Solution {
public:

    void dfs(vector<vector<int>>& rooms, vector<int> &visited, int room)
    {
        // 处理当前结点
        visited[room] = 1;  // 已访问
        // 遍历该房间存在的钥匙
        // 判断剩余结点
        for (int i = 0; i < rooms[room].size(); i++){
            int x = rooms[room][i];
            // x房间还没去过,就去x房间
            if(visited[x] == 0){
                dfs(rooms,visited,x);
            }
        }
    }

    bool canVisitAllRooms(vector<vector<int>>& rooms) {
        int n = rooms.size();
        vector<int> visited(n,0);   // 访问过为1,未访问为0
        dfs(rooms,visited,0);
        int res = 0;
        for (int x:visited){
            res += x;
        }
        return n == res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hyzhang_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值