队列中取最大值操作

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

  

时间: 2024-10-01 04:59:35

队列中取最大值操作的相关文章

编程之美之队列中取最大值操作

问题: 假设有这样一个拥有3个操作的队列: 1. EnQueue(v): 将v加入队列中 2. DeQueue(): 使队列中的队首元素删除并返回此元素 3. MaxElement: 返回队列中的最大元素 设计一种数据结构和算法,让MaxElement操作的时间复杂度尽可能地低. #include<iostream> #include<limits.h> using namespace std; class Stack { public: Stack() { stackTop =

编程之美---队列中取最大值操作问题

如何快速获取队列中的最大值? 最简单的办法,用一个for循环遍历,复杂度为o(n). 解法二:用大顶堆来实现,复杂度为哦o(1),但是入队和出队复杂度变为o(logN),堆中的每一个元素还得有个指针指向它的后继元素. 解法三:可以使用两个栈来模拟队列,从右边的栈进入元素相当于入队,出队时,只有当左边的栈为空时,才把右边栈的元素全部出栈到左边的栈. 1 class stack 2 { 3 public: 4 stack() 5 { 6 stackTop = -1; 7 maxItemIndex =

编程之美——队列中取最大值操作

为实现O(1)的时间复杂度完成取队列中最大元素,使用maxStackItemIndex记录队列(使用两个栈实现)中最大元素下标,使用数组link2NextMaxItem[]记录数组中次大值的下标,这也就是使用两个栈(先进后出)模拟队列二不是直接使用队列(先进先出)的原因:先进后出可以保证当执行pop操作(pop出最大值)时可以更新maxStackItemIndex=link2NextMaxItem[stackTop]:而队列则不具有这种回溯特性: 代码: 1 #include<iostream>

队列中取最大值操作问题

问题: 假设有这样一个拥有3个操作的队列: 1. EnQueue(v): 将v加入队列中 2. DeQueue(): 使队列中的队首元素删除并返回此元素 3. MaxElement: 返回队列中的最大元素 设计一种数据结构和算法,让MaxElement操作的时间复杂度尽可能地低. 思路: (1)用两个栈设计一个新的数据类型(数据类型定义为MyStack),其中一个栈用来存放数据,另一个栈用来存放最大值, 当插入数据时,第一个栈接受数据进行入栈操作,第二栈首先判断一下栈顶元素和插入元素的大小,如果

&lt;&lt;编程之美&gt;&gt; -- 队列中取最大值操作的问题

不得不说编程之美是一本好书,虽然很多题目在做acm中的过程中遇到过,不过还是有很多值得思考的地方 这是今天在编程之美上看到的一个问题,对于栈转化成队列的一个思考 平时都太过依赖c++内函数库中的栈和队列,但是对于他们的扩展我们还是应该自己进行手写栈和队列来实现更简单的算法 题目大意: 假设有这样一个拥有3个操作的队列: 1. EnQueue(v) : 将 v 加入队列 2. DeQueue: 使队列中队首元素删除并返回此元素 3.MaxElement: 返回队列中的最大元素 设计一种数据结构和算

3.7 队列中取最大值操作问题

问题: 假设有这样一个拥有3个操作的队列: 1. EnQueue(v): 将v加入队列中 2. DeQueue(): 使队列中的队首元素删除并返回此元素 3. MaxElement: 返回队列中的最大元素 设计一种数据结构和算法,让MaxElement操作的时间复杂度尽可能地低. 方法:用两个栈来模拟队列 在代码中,maxStackItemIndex代表栈中最大的数的下标 link2NextMaxItem[index]表示当前index这个下标代表的数字(当前是最大的)如果没有的话,那么最大的那

程之美第3章结构之法-字符串及链表的探索3.7 队列中取最大值操作问题

#include<iostream> #include<vector> using namespace std; class stack { private: vector<int> vec;//用来保存当前进栈的值 vector<int> max_vec; public: void push(int a) { vec.push_back(a); if(max_vec.size()==0||a>vec[max_vec[max_vec.size()-1]

【编程之美】3.7 队列取最大值操作问题 ☆

之前写过栈的,以为队列的也一样,结果一点都不一样.写了好久啊. 因为栈是后进先出,先进去的数字不会影响后面的数字:而队列是先进先出,后进去的会受先进入的数字的影响. 比如: (先)  1 9 3 8 4 (后)  这样的序列 栈存储        1 9               就可以了,因为9弹出后,自然 1 就是最大的 队列则不行,如果按上面存储9弹出后 剩下 3 8 4,8是最大的,没有存储. 我的方法,保存一个max的队列 入队列时: 如果新值比 max的最前面的元素大,那么把max

在含有null值的复杂类的集合(Collection)中取最大值

在日常编程中,经常遇到要在一组复杂类的集合(Collection)中做比较.取最大值或最小值. 举个最简单的例子,我们要在一个如下结构的集合中选取包含最大值的元素: public class Class<T> where T : struct { public T? Value { get; set; } } var ints = new List<Class<int>>() { new Class<int>() { Value = 2 }, new Cla