假设有这样一个拥有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()); }