LeeCode刷题记录——栈

本系列作为本人刷LeeCode记录,主要记录思路,作为备忘,仅供参考。(啊,算法实在太难了。。。万里长征第一步,脑袋已经变糊涂)

文章目录

    • 1、最小栈
        • 思路一:使用两个栈,一个数据栈s,一个辅助栈min,min.top()用来存放最小值。
        • 思路二:使用链栈的方式,构建两个值,一个当前节点的值val,一个是当前以此节点为"栈顶的栈内最小值"min,始终更新这个值(用作对比大小和返回最小值)。
    • 2、用队列实现栈
        • 思路一:使用一个队列que,将前size-1个pop出重新压入到队列中,变成一个新的队列que_new,que_new的队首即为栈顶的值。
        • 思路二:双队列q1、q2,正常push到q1,然后将前size-1个push到q2,q1只剩下队尾的值,即栈顶的值。pop掉之后,然后将q1和q2交换,q1=q2,让q1恢复正常。q2就是临时中转站的意思。

1、最小栈

思路一:使用两个栈,一个数据栈s,一个辅助栈min,min.top()用来存放最小值。

  • 【进栈】:在push 值x过程中,如果栈s为空,或者x小于等于min.top值的话,就把x也push到min.top()中,再将x push到s.top中;【注意为什么是小于等于:如果push 这三个数 0,1,0的话,在<的情况下,辅助栈是只有一个0的。那么如果我pop( )后,此时辅助栈就为空了。在调用getMin( )就无法返回辅助栈的top元素了
  • 【出栈】:pop过程中,注意,如果辅助栈min.top和数据栈s.top中的值一样大,那么min.top的值也要pop掉。
  • 最小值始终是min.top的值

时间复杂度:O(1),“出栈”、“入栈”、“查看栈顶元素”的操作不论数据规模多大,都只是有限个步骤,因此时间复杂度是:O(1)。
空间复杂度:O(N),这里 N 是读出的数据的个数。

class MinStack {
public:
    stack<int> s;   // 源栈
    stack<int> min; // 辅助栈,用来存放最小值
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        s.push(x);
        // 如果min栈为空,或者x <= min.top。则把x压入辅助栈min中
        if(min.empty() || x<=min.top()){
            min.push(x);
        }
    }
    // 如果s中pop的值和辅助栈min中一样大,则min中top值也要仍掉,以保证min中为最小值
    void pop() {
        if(s.top() == min.top())
            min.pop();
        s.pop();
    }
    // 直接返回s的top值
    int top() {
        return s.top();        
    }
    // min中的top即是最小值
    int getMin() {
        return min.top();
    }
};

思路二:使用链栈的方式,构建两个值,一个当前节点的值val,一个是当前以此节点为"栈顶的栈内最小值"min,始终更新这个值(用作对比大小和返回最小值)。

  • 【进栈】:若空栈,直接压入节点(x,x)。若不是空栈,则先判断tmp,即x和之前栈顶最小值min的大小,然后将更新节点最小值(x,tmp),将新节点压入栈顶,更新头节点。
  • 【出栈】:让头节点指向自身的下一个节点即可,我们也不用管出栈之后的最小值变化,即使当前出栈元素就是最小值也无妨,因为每个节点的 min 值记录的都是栈底到此节点的元素最小值。即出栈的val和min互不影响。
  • top元素就是:head->val。
  • getmin就是:head->min。
    【总结:不得不说,线性表是最基本的结构,要深入理解!!】
class MinStack {
private:
    struct Node{
        int val;   // 当前节点的值
        int min;   // 当前以此节点为栈顶的栈内最小元素的值
        Node *next;   // 下一个节点

        Node(int x, int y) : val(x), min(y), next(nullptr) {}
    };
    Node *head;   // 声明头节点

public:
    /** initialize your data structure here. */
    MinStack() {
        head = nullptr;   // 初始化头节点
    }

    void push(int x) {
        // 若栈空,则申请新节点空间并赋予头节点(相当于当前节点入栈)
        if(head == NULL){
            head = new Node(x, x);
        }
        // 若栈非空,则更新新节点的栈内元素最小值后,将新节点插入栈顶,最后更新头节点
        else{
            int tmp = x < head->min ? x : head->min;
            Node *cur = new Node(x, tmp);
            cur->next = head;  	// 将新节点插入栈顶
            head = cur; 		// 更新头节点
        }
    }

    void pop() {
        // 让头节点指向自身的下一个节点即可
        // 不用管出栈之后的最小值变化,即使当前出栈元素就是最小值也无妨,
        // 因为每个节点的 min 值记录的都是栈底到此节点的元素最小值
        head = head->next;
    }

    int top() {
        // 直接返回头节点 val 值即可,头节点永远指向栈顶
        return head->val;
    }

    int getMin() {
        // 直接返回头节点 min 值即可,头节点的 min 值永远是栈内所有元素的最小值
        return head->min;
    }
};

2、用队列实现栈

思路一:使用一个队列que,将前size-1个pop出重新压入到队列中,变成一个新的队列que_new,que_new的队首即为栈顶的值。

  • 【push】:直接压入队列,在压入新元素x之后,将前que.size-1个元素重新压到队列,使其队尾(即栈顶)为新队列的front。
  • 【pop】:拿到que_new的front,即栈顶值top,然后que.pop
  • 【top】:即队que_new的front
  • 【empty】:即队列的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);
        // 在压入新元素x之后,将前que.size-1个元素重新压到队列,使其队尾(即栈顶)为新队列的front
        for(int i=0; i<que.size()-1; i++){
            que.push(queue.front());
            que.pop();
        }
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int val = top(); // 拿到栈顶值,即新队列的front
        que.pop(); // 
        return val;
    }
    
    /** Get the top element. */
    int top() {
        return que.front();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return que.empty();
    }
};

思路二:双队列q1、q2,正常push到q1,然后将前size-1个push到q2,q1只剩下队尾的值,即栈顶的值。pop掉之后,然后将q1和q2交换,q1=q2,让q1恢复正常。q2就是临时中转站的意思。

  • 【push】:正常直接push到队列q1
  • 【pop】:将前size-1个push到q2,q1只剩下队尾的值,即栈顶的值。pop掉之后,然后将q1和q2交换,q1=q2,让q1恢复正常
    【总结:内存消耗比单队列肯定是多一点。】
    LeeCode刷题记录——栈_第1张图片
class MyStack {
//双队列实现栈
public:
    /** Initialize your data structure here. */
    MyStack() {

    }
    
    /** Push element x onto stack. */
    void push(int x) {
        q1.push(x);
        topValue = x; // 保存栈顶值,topValue
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        // 将队列q1 size-1前的值push到q2
        while(q1.size()>1){
            topValue = q1.front(); // 循环结束时topValue仍然是栈顶值,即队列q1的尾值
            q2.push(topValue);
            q1.pop();
        }
        // 此时q1只剩下一个值,即栈顶值,pop掉
        int result = q1.front();
        q1.pop();
        // 将q1恢复
        queue<int> tmp = q1;
        q1 = q2;
        q2 = tmp;
        return result;
    }
    
    /** Get the top element. */
    int top() {
        return topValue;
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return q1.empty();
    }

private:
    queue<int> q1;
    queue<int> q2; // 中转队列
    int topValue;
};

你可能感兴趣的:(LeeCode刷题)