假设有这样一个拥有3个操作的队列:
1.EnQueue(v):将v加入队列
2.DeQueue:使队列中的队首元素删除并返回元素
3.MaxElement:返回队列中的最大元素
请设计一种数据结构和算法,让MaxElement操作的时间复杂度尽可能低
研究这个问题之前,先研究两个子问题:
1、设计一个包含min操作的栈
2、用栈实现队列
一、设计一个包含min操作的栈
考虑给栈增加一个成员变量MinValue,有元素入栈的时候,将入栈元素与MinValue相比,如果小于MinValue,用入栈元素的值,更新MinValue,但是效率低的地方在于,如果出栈元素等于MinValue,则需要重新查找整个栈,找出MinValue。
解决这个问题的方法是采用一个辅助栈,将一个元素入栈的时候,在辅助栈中相同的位置记录它入栈之前栈中最小元素的位置。出栈的时候,若出栈元素等于MinValue,则查找这个辅助栈对应项的元素,直接按位置取出最小值。
如图:
最左边的数字代表元素在栈中的位置。当3入栈时,它就是最小元素,因此在辅助栈中与它位置相同的地方,保存它之前的最大位置(-1或0皆可),4入栈的时候,考虑到3和help[0]相等,因此4对应的辅助栈元素是0,2入栈时,考虑到4大于stack[help[1]],因此2对应的辅助栈元素仍然是0,1入栈时,由于2比stack[help[2]]小,因此1对应的辅助栈元素是2的位置,即2.依次类推。
源代码:
template<typename T> class MinStack {
private:
T *stack;
T *min;
size_t top;
T MinValue;
size_t MaxElement;
public:
MinStack();
~MinStack();
void push(T t);
void pop();
T GetTop();
T GetMin();
};
template<typename T> MinStack<T>::MinStack()
{
MaxElement = 20;
stack = new T[MaxElement];
min = new T[MaxElement];
top = -1;
}
template<typename T> MinStack<T>::~MinStack()
{
delete[] stack;
delete[] min;
}
template<typename T> void MinStack<T>::push(T t)
{
if(top == MaxElement)
{
cout<<"It's full";
return;
}
if(top == -1)
{
stack[++top] = t;
min[top] = 0;
MinValue = t;
}
else
{
stack[++top] = t;
min[top] = stack[top-1]>stack[min[top-1]]?min[top-1]:top-1;
MinValue = t>stack[min[top]]?stack[min[top]]:t;
}
}
template<typename T> void MinStack<T>::pop()
{
if(top==-1)
{
cout<<"It's empty";
return;
}
if(top == 0)
{
--top;
}
else
{
--top;
MinValue = stack[min[top+1]];
}
}
template<typename T> T MinStack<T>::GetTop()
{
if(top==-1)
{
cout<<"It's empty stack";
exit(0);
}
return stack[top];
}
template<typename T> T MinStack<T>::GetMin()
{
if(top==-1)
{
cout<<"It's empty stack";
exit(0);
}
return MinValue;
}
二、用两个栈实现队列
这个问题的关键是,设置一个栈用来入队,另一个栈用来出队。一开始出栈队为空,当有出队动作的时候,就将入队栈全部出栈,进栈到出队栈,这样顺序就正好反过来了,下一次出队就直接从出队栈取元素,入队则继续入入队栈。总结一下就是:
入队一直是在入队栈入栈。
出队的时候,如果出队栈为空,就把入队栈全部出栈入栈到出队栈,再取栈顶元素,移动栈顶指针,如果出队栈不空,直接取元素,移指针。
源代码:
template<typename T> class Stack
{
public:
T *s;
size_t top;
size_t Max;
Stack();
~Stack();
void push(T t);
void pop();
T GetTop();
};
template<typename T> class Queue
{
private:
Stack<T> En;
Stack<T> De;
public:
void EnQueue(T t);
T DeQueue();
};
template<typename T> Stack<T>::Stack()
{
Max = 20;
s = new T[Max];
top = -1;
}
template<typename T> Stack<T>::~Stack()
{
delete[] s;
}
template<typename T> void Stack<T>::push(T t)
{
if(top==Max)
{
cout<<"It's full stack";
return;
}
s[++top] = t;
}
template<typename T> void Stack<T>::pop()
{
if(top == -1)
{
cout<<"It's empty stack";
return;
}
--top;
}
template<typename T> T Stack<T>::GetTop()
{
return s[top];
}
template<typename T> void Queue<T>::EnQueue(T t)
{
En.push(t);
}
template<typename T> T Queue<T>::DeQueue()
{
if(De.top==-1)
{
while(En.top+1>0)
{
De.push(En.GetTop());
En.pop();
}
}
if(De.top==-1)
{
cout<<"It's empty queue";
}
T temp = De.GetTop();
De.pop();
return temp;
}
解决了这两个子问题之后,队列中取最大值操作便迎刃而解。
修改Queue的定义,用MinStack代替Stack,并加入一个MinValue()函数。
template<typename T> class Queue
{
private:
MinStack<T> En;
MinStack<T> De;
public:
void EnQueue(T t);
T DeQueue();
T MinValue();
};
template<typename T> T Queue<T>::MinValue()
{
if(De.top==-1&&En.top==-1)
{
cout<<"It's a empty queue";
exit(0);
}
else if(De.top == -1)
{
return En.MinValue;
}
else if(En.top == -1)
{
return De.MinValue;
}
else
{
return En.MinValue<De.MinValue?En.MinValue:De.MinValue;
}
}
如果不用模板,直接用int或者float,则可以将MinStack中的MinValue值初始化为INT_MAX,并且如果队空就将MinValue置为INT_MIN,这样可以省略En空,De不空或者En不空De空的讨论。
//问题:设计一个队列能够在O(1)时间内取得队列的最大值
#include <stdio.h>
#include <queue>
#include <stack>
//O(1)的速度取出栈中的最大值
template<typename T>
class MaxStack
{
public:
//入栈
void Push(const T& value)
{
data_.push(value);
if (max_element_.empty())
{
max_element_.push(value);
}
else if (value >= max_element_.top())
{
max_element_.push(value);
}
}
//返回栈顶元素
T Top()
{
return data_.top();
}
//出栈
void Pop()
{
if (data_.top() == max_element_.top())
{
max_element_.pop();
}
data_.pop();
}
//判断是否为空
bool Empty()
{
return data_.empty();
}
//取出最大值
T Max()
{
if (!max_element_.empty())
{
return max_element_.top();
}
}
private:
std::stack<T> data_;
std::stack<T> max_element_;
};
//O(1)的速度取出队列中的最大值
template<typename T>
class MaxQueue
{
public:
//入队操作!!!!
void Push(const T& value)
{
push_stack_.Push(value);
}
//取队首元素
T Front()
{
if (pop_stack_.empty())
{
while (!push_stack_.Empty())
{
pop_stack_.Push(push_stack_.Top());
push_stack_.Pop();
}
}
return pop_stack_.Top();
}
//出队操作!!!!
void Pop()
{
if (pop_stack_.Empty())
{
while (!push_stack_.Empty())
{
pop_stack_.Push(push_stack_.Top());
push_stack_.Pop();
}
}
pop_stack_.Pop();
}
//判空操作!!!!!
bool IsEmpty()
{
return push_stack_.Empty() && pop_stack_.Empty();
}
//取出最大值
T Max()
{
if (!push_stack_.Empty() && !pop_stack_.Empty())
{
return push_stack_.Max() > pop_stack_.Max() ? push_stack_.Max() : pop_stack_.Max();
}
else if (push_stack_.Empty() && !pop_stack_.Empty())
{
return pop_stack_.Max();
}
else if (!push_stack_.Empty() && pop_stack_.Empty())
{
return push_stack_.Max();
}
else
{
throw RUNTIME_ERROR;
}
}
private:
MaxStack<T> push_stack_;
MaxStack<T> pop_stack_;
};
//测试用例
int main(int argc, char** argv)
{
MaxQueue<int> max_queue;
max_queue.Push(1);
max_queue.Push(2);
max_queue.Push(6);
max_queue.Push(4);
max_queue.Push(5);
max_queue.Push(2);
printf("max %d\n", max_queue.Max());
max_queue.Pop();
printf("max %d\n", max_queue.Max());
max_queue.Pop();
printf("max %d\n", max_queue.Max());
max_queue.Pop();
printf("max %d\n", max_queue.Max());
max_queue.Pop();
printf("max %d\n", max_queue.Max());
max_queue.Pop();
printf("max %d\n", max_queue.Max());
}