LeetCode【剑指offer】系列(栈与队列篇)

剑指offer09.两个栈实现队列

题目链接

题目:读者来到图书馆排队借还书,图书管理员使用两个书车来完成整理借还书的任务。书车中的书从下往上叠加存放,图书管理员每次只能拿取书车顶部的书。排队的读者会有两种操作:

  • push(bookID):把借阅的书籍还到图书馆。
  • pop():从图书馆中借出书籍。

为了保持图书的顺序,图书管理员每次取出供读者借阅的书籍是 最早 归还到图书馆的书籍。你需要返回 每次读者借出书的值 。

如果没有归还的书可以取出,返回 -1

思路:模拟即可。用一个栈保存所有元素,每次需要队首元素时,将这个栈的元素倒出到另一个栈里,即得到逆序排列。取出队首元素再把元素倒回原来的栈即可。

通过代码:

class CQueue {
private:
    stack<int> s1,s2;
public:
    CQueue() {
    }
    
    void appendTail(int value) {
        s1.push(value);
    }
    
    int deleteHead() {
        if(s1.empty())
            return -1;
        while(!s1.empty())
        {
            s2.push(s1.top());
            s1.pop();
        }
        int res = s2.top();
        s2.pop();
        while(!s2.empty())
        {
            s1.push(s2.top());
            s2.pop();
        }
        return res;
    }
};

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue* obj = new CQueue();
 * obj->appendTail(value);
 * int param_2 = obj->deleteHead();
 */

剑指offer30.包含min函数的栈

题目链接

题目:请你设计一个 最小栈 。它提供pushpoptop操作,并能在常数时间内检索到最小元素的栈。

实现MinStack类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

思路一:辅助栈。设计一个数据结构,使得每个元素 a 与其相应的最小值 m 时刻保持一一对应。因此我们可以使用一个辅助栈,与元素栈同步插入与删除,用于存储与每个元素对应的最小值。

通过代码:

class MinStack {
private:
    stack<int> valstk, minstk;
public:
    /** initialize your data structure here. */
    MinStack() {
    }
    
    void push(int x) {
        valstk.push(x);

        if(minstk.empty())
            minstk.push(x);
        else if(minstk.top() < x)
                minstk.push(minstk.top());
        else
            minstk.push(x);
    }
    
    void pop() {
        valstk.pop();
        minstk.pop();
    }
    
    int top() {
        return valstk.top();
    }
    
    int getMin() {
        return minstk.top();
    }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

思路二:差值栈。不需要额外的辅助空间。栈中的每一层存的是比下面所有层中最小值大多少。例如,当初始栈为空时,最小值即为当前元素x,0入栈。当新的元素x要入栈时,入的是比min大多少,即x-min,然后更新min。

通过代码:

class MinStack {
private:
    stack<long long> stk;
    long long min_value;
public:
    /** initialize your data structure here. */
    MinStack() {
    }
    
    void push(int x) {
        if(stk.empty())
        {
            stk.push(0);
            min_value = x;
        }
        else
        {
            stk.push(x - min_value);
            min_value = min(min_value, (long long)x);
        }
    }
    
    void pop() {
        if(stk.top() <= 0)
            min_value -= stk.top();
        stk.pop();
    }
    
    int top() {
        if(stk.top() > 0)
            return stk.top() + min_value;
        else
            return min_value;
    }
    
    int getMin() {
        return min_value;
    }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

剑指offer31.栈的压入、弹出序列

题目链接

题目:现在图书馆有一堆图书需要放入书架,并且图书馆的书架是一种特殊的数据结构,只能按照一定的顺序放入和拿取书籍。

给定一个表示图书放入顺序的整数序列putIn,请判断序列takeOut是否为按照正确的顺序拿取书籍的操作序列。你可以假设放入书架的所有书籍编号都不相同。

思路:用一个栈模拟即可。按照putIn序列顺序入栈。每次入栈后,循环判断栈顶元素 == 拿取序列的当前元素是否成立,将符合拿取序列顺序的栈顶元素全部拿取。

通过代码:

class Solution {
public:
    bool validateBookSequences(vector<int>& putIn, vector<int>& takeOut) {
        stack<int> stk;
        int j = 0;
        for(int i = 0; i < putIn.size(); i++)
        {
            stk.push(putIn[i]);
            while(!stk.empty() && stk.top() == takeOut[j])
            {
                stk.pop();
                j++;
            }
        }
        return stk.empty();
    }
};

剑指offer59-II.队列的最大值

题目链接

题目:请设计一个自助结账系统,该系统需要通过一个队列来模拟顾客通过购物车的结算过程,需要实现的功能有:

  • get_max():获取结算商品中的最高价格,如果队列为空,则返回 -1
  • add(value):将价格为value 的商品加入待结算商品队列的尾部
  • remove():移除第一个待结算的商品价格,如果队列为空,则返回 -1

注意,为保证该系统运转高效性,以上函数的均摊时间复杂度均为O(1)

思路:如果用普通队列,在获取最大值时需要将整个队列出队再入队,从而导致该操作时间复杂度为O(n)。所以,如果要实现O(1)的时间复杂度,需要借助一个用于辅助的单调队列。这就类似于滑动窗口最大值问题了。

考虑利用deque数据结构来实现。如下图所示,考虑构建一个递减列表来保存队列所有递减的元素 ,递减列表随着入队和出队操作实时更新,这样队列最大元素就始终对应递减列表的首元素,实现了获取最大值O(1)时间复杂度。

通过代码:

class Checkout {
private:
    queue<int> q;
    deque<int> dq;
public:
    Checkout() {
    }
    
    int get_max() {
        if(q.empty())
            return -1;
        return dq.front();
    }
    
    void add(int value) {
        q.push(value);
        while(!dq.empty() && dq.back() <= value)
            dq.pop_back();
        dq.push_back(value);
    }
    
    int remove() {
        if(q.empty())
            return -1;
        int res = q.front();
        q.pop();
        if(res == dq.front())
            dq.pop_front();
        return res;
    }
};

/**
 * Your Checkout object will be instantiated and called as such:
 * Checkout* obj = new Checkout();
 * int param_1 = obj->get_max();
 * obj->add(value);
 * int param_3 = obj->remove();
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

h0l10w

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

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

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

打赏作者

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

抵扣说明:

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

余额充值