zoukankan      html  css  js  c++  java
  • 队列中取最大值操作

    假设有这样一个拥有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
    ", max_queue.Max());
    	max_queue.Pop();
    	printf("max %d
    ", max_queue.Max());
    	max_queue.Pop();
    	printf("max %d
    ", max_queue.Max());
    	max_queue.Pop();
    	printf("max %d
    ", max_queue.Max());
    	max_queue.Pop();
    	printf("max %d
    ", max_queue.Max());
    	max_queue.Pop();
    	printf("max %d
    ", max_queue.Max());
    }
    

      

  • 相关阅读:
    JTable使用
    VirtualBox 虚拟机安装
    ICONIX简介
    反射在Java Swing编程中的应用之java 模仿.net事件处理
    【Ubuntu】UBUNTU中如何获得root权限
    html头部代码【转载】
    eclipse code templates 设置
    include与jsp:include区别【转载】
    银行卡
    【AJAX】DWR入门教程
  • 原文地址:https://www.cnblogs.com/Vae1990Silence/p/4496966.html
Copyright © 2011-2022 走看看