剑指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函数的栈
题目:请你设计一个 最小栈 。它提供push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
实现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()
:获取结算商品中的最高价格,如果队列为空,则返回 -1add(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();
*/