[编程之美] PSet3.7 队列中取最大值操作问题

来源:互联网 发布:opengl用什么软件 编辑:程序博客网 时间:2024/05/10 09:53
问题描述:

       假设有这样一个拥有3个操作的队列:
      1. EnQueue(v): 将v加入队列中
      2. DeQueue(): 使队列中的队首元素删除并返回此元素
      3. MaxElement: 返回队列中的最大元素

      设计一种数据结构和算法,让MaxElement操作的时间复杂度尽可能地低。

思路:

      解法一:传统队列

      传统队列的方式,采用数组或链表来存储队列的元素,利用指针遍历整个队列元素,在长度为N的队列,时间复杂度O(N)。对于小飞的思路,在Push处维护一个MaxVal,方法是可以的,但是需要在Pop函数处考虑是否弹出了最大值,如果弹出了,需要重新遍历队列一次查找最大值。解法一代码如下:

//方法一:普通队列//用链表作为队列基础数据结构struct List{int data;List *next;};class Queue{private:List *front;List *rear;public:Queue();//默认构造函数~Queue();//析构函数bool isEmpty();//队列是否为空int DeQueue();//从队列头部弹出元素void EnQueue(int elem);//向队列尾部压入元素void traverse();//遍历队列int MaxElem();//取最大值操作};Queue::Queue(){front = new List;//头结点,不存放数据front->next = NULL;rear = front;}Queue::~Queue(){//---将头结点也析构掉if(rear == front)delete front;else{List *p = front;List *q = p->next;while(p != rear){delete p;p=q;q=q->next;}delete rear;}rear = front = NULL;}bool Queue::isEmpty(){if(front == rear)return true;return false;}void Queue::EnQueue(int elem){List *temp = new List;temp->data = elem;temp->next = NULL;//从队尾压入元素rear->next = temp;rear = temp;}int Queue::DeQueue(){//---判断是否为空队列if(isEmpty())return -9999;//---保存需要删除的指针List *temp = front->next;int val = temp->data;front->next = temp->next;if(temp == rear){//如果队列只有最后一个元素,则置rear为frontrear = front;}delete temp;return val;}int Queue::MaxElem(){List *temp = front->next;//第一个元素int maxVal = -9999;while(temp != NULL){if(temp->data > maxVal)maxVal = temp->data;temp = temp->next;}return maxVal;}void Queue::traverse(){if(front == rear){//队列为空cout<<"队列为空!"<<endl;return;}List *temp = front->next;//第一个节点cout<<"队列元素为:";while(temp != NULL){cout<<temp->data<<" ";temp = temp->next;}cout<<endl;}int main(){Queue q;for(int i=6 ; i>=0 ; i--)q.EnQueue(i);q.traverse();cout<<q.MaxElem()<<endl;return 0;}

      解法二:最大堆法

      考虑用最大堆维护队列中元素,堆中的每个元素都有指针指向它的后继元素。这样MaxElement操作就是维护这个最大堆的过程,时间复杂度O(1),入队和出队操作时间复杂度O(logN)。代码如下:

       新思路:辅助数组法可以快速取得栈的最大值

      由于栈的取数和存数操作都在栈顶,所以维护栈中最大值很容易!用一个辅助数组记住栈在当前状态(辅助数组下标表示)下最大值所在位置,这样Max操作的时间复杂度O(1),相当于用空间复杂度换区了时间复杂度。

      解法三:用两个栈来实现

      用两个栈(新思路所示)来实现一个队列,因为入栈一次相当于对原数据进行逆序一次,而入栈两次则维持原数据顺序。具体做法为设有A、B两栈,则在未取数据之前将数据压入B中缓存起来,当取数时,如果栈A数据为空,则将栈B中数据统统压入栈A中。这种有辅助数组的栈可以让平均复杂度为线性。代码如下:

//方法三:两个栈建立队列//可以在平均复杂度O(1)的情况下取出最大值,入队和出队的平均复杂度为O(1)(摊还分析)//因为每个元素最多有可能移动了三次:压入栈A,从栈A压入栈B,从栈B弹出。//底层数据结构用vectorclass Stack{private://top指针可以不要,因为vector提供了back与pop_back函数,可以用size()-1替代顶指针vector<int> data;vector<int> MaxIndex;public:Stack (){}~Stack(){}void Push(int elem);int Pop();int MaxElem();bool isEmpty();void traverse();void Inv_traverse();};void Stack::Push(int elem){//---如果栈为空或当前元素大于栈中最大元素,更新最大值下标数组if(data.empty() || elem>data[MaxIndex[MaxIndex.size()-1]])MaxIndex.push_back(data.size());data.push_back(elem);}bool Stack::isEmpty(){return data.empty();}int Stack::Pop(){if(isEmpty()){cout<<"栈为空!"<<endl;return INT_MIN;}if(data.size()-1 == MaxIndex[MaxIndex.size()-1])//如果弹出的是当前最大值MaxIndex.pop_back();int val = data[data.size()-1];data.pop_back();return val;}int Stack::MaxElem(){if(isEmpty()){//栈为空return INT_MIN;}return data[MaxIndex[MaxIndex.size()-1]];}void Stack::traverse(){if(isEmpty()){cout<<"栈为空"<<endl;return;}for(int i=data.size()-1 ; i>=0 ; i--)//不能为size_t,因为size_t为unsigned int,从0减一过后并不为负数,而是一个非常大的正数cout<<data[i]<<" ";cout<<endl;}void Stack::Inv_traverse(){if(isEmpty()){cout<<"栈为空"<<endl;return;}for(int i=0 ; i<data.size() ; i++)cout<<data[i]<<" ";cout<<endl;}//---用两个有辅助数组的栈实现队列class Queue{private:Stack s1,s2;public:Queue(){}~Queue(){}int MaxElem();void EnQueue(int elem);int DeQueue();bool isEmpty();void traverse();};int Queue::MaxElem(){return s1.MaxElem()>s2.MaxElem()?s1.MaxElem():s2.MaxElem();}bool Queue::isEmpty(){return s1.isEmpty()&&s2.isEmpty() ;}void Queue::EnQueue(int elem){//---入队时将所有元素存放在s2中s2.Push(elem);}int Queue::DeQueue(){//---如果s1空了,则将s2缓存区中数据压入s1中if(s1.isEmpty()){while(!s2.isEmpty())s1.Push(s2.Pop());}return s1.Pop();}void Queue::traverse(){if(isEmpty()){//如果队列为空cout<<"队列为空"<<endl;return;}else if(s1.isEmpty()){//s1空s2非空s2.Inv_traverse();}else{s1.traverse();}}int main(){Queue q;q.EnQueue(3);q.EnQueue(19);q.EnQueue(8);q.EnQueue(1);q.EnQueue(5);q.EnQueue(6);cout<<q.DeQueue()<<endl;//从队头弹出元素q.traverse();cout<<q.MaxElem()<<endl;return 0;}

0 0
原创粉丝点击