编程之美:第三章 结构之法 3.7队列中取最大值操作问题

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

请设计一种数据结构和算法,让MaxElement操作的时间复杂度尽可能地低。
队列底层的数据结构不一定要用数组来实现,还可以使用其他特殊的数据结构来实现。

这道题目:
参见剑指。可以用两个栈来实现。(剑指上的题目是包含min功能的栈,这道题目是包含max功能的队列)
一个用作数据栈,一个用作存储最大值的栈。存储最大值的栈中,我们这样设计,如果最大栈为空,那么将当前元素直接压入最大栈中;如果已经有了元素,
那么我们将当前值与栈顶比较,如果栈顶>=当前值,再向栈中压入当前值;如果栈顶<当前值,将当前值压入;
弹出的时候,我们就将最大栈里面的值弹出即可。
而数据栈则是存放所有数据。

解法三:
由于栈是和队列相似的数据结构,先看看栈。
这里,维护一个最大值的序列(link2NextMaxItem)来保证Max操作的时间复杂度为O(1),相当于用空间复杂度换取时间复杂度。

输入:
4
2 1 3 4
输出:
4
*/

/*
关键:
1 这里,维护一个最大值的序列(link2NextMaxItem)来保证Max操作的时间复杂度为O(1),相当于用空间复杂度换取时间复杂度。
2 stack():_iStackTop(-1),_iMaxIndex(-1){}//设置栈顶为-1,最大值下标元素为-1
3 		if(_iMaxIndex >= 0)//如果最大值下标>=0,表示可以返回栈数组中对应最大值下标的元素,否则返回负无穷大
		{
			return _itemArr[_iMaxIndex];
4 	void push(T x)//压入算法:首先使栈顶累加,然后判断栈顶是否超过最大值;放入元素,将放入的元素与栈中的最大值进行比较,如果大于最大值,那么
		//使最大值序列中的栈顶存放最大值下标,同时使最大值下标为栈;                                                       小于      ,那么
		//使最大值序列栈顶元素对应的值为-1
	{
		_iStackTop++;
5 			if(_iStackTop == _iMaxIndex)//记住,返回栈顶最大值元素之后,要重新修改链表,更新最大值下标为链表中下一个元素,并使栈顶减减
			{
				_iMaxIndex = _iLinkNextMaxItem[_iStackTop];
			}
			_iStackTop--;
6 		if(x > max())
		{
			_iLinkNextMaxItem[_iStackTop] = _iMaxIndex;//采用类似头插法的方式来保存最大元素,使_iLinkNextMaxItem[_iStackTop]中始终保存的是最大值下标
			_iMaxIndex = _iStackTop;
		}
		else
		{
			_iLinkNextMaxItem[_iStackTop] = -1;//如果不是最大值,就始终维持最大值下标为-1
		}
7 	Stack<T> stackA;//注意标准是stack,queue,小写
	Stack<T> stackB;//注意,这里调用的是我们自己写的栈
8 	void EnQueue(T x)
	{
		stackB.push(x);
	}
	T DeQueue()
	{
		if(stackA.empty())//如果队列A空了,就将B中的元素全部放入A中
		{
			while(!stackB.empty())//每个元素被移动的次数最多3次:从B堆栈进入,当A堆栈为空时,从B堆栈弹出并压入A堆栈,从A堆栈弹出
			{
				stackA.push(stackB.pop());
			}
		}
		return stackA.pop();//返回A中弹出的栈顶元素
9 	T max()
	{
		return maxValue(stackA.max(),stackB.max());//牛逼,用递归来做
*/

#include <stdio.h>
#include <stack>
#include <queue>
#include <iostream>



const int INT_MAX1 = 0x7fffffff;//靠名字冲突了
const int INT_MIN1 = -INT_MAX1;
const int MAXSIZE = 10000;

using namespace std;

template<typename T>
class Stack
{
public:
	Stack():_iStackTop(-1),_iMaxIndex(-1){}//设置栈顶为-1,最大值下标元素为-1
	bool empty()
	{
		if(_iStackTop < 0)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	T max()
	{
		if(_iMaxIndex >= 0)//如果最大值下标>=0,表示可以返回栈数组中对应最大值下标的元素,否则返回负无穷大
		{
			return _itemArr[_iMaxIndex];
		}
		else
		{
			return INT_MIN1;
		}
	}
	void push(T x)//压入算法:首先使栈顶累加,然后判断栈顶是否超过最大值;放入元素,将放入的元素与栈中的最大值进行比较,如果大于最大值,那么
		//使最大值序列中的栈顶存放最大值下标,同时使最大值下标为栈;                                                       小于      ,那么
		//使最大值序列栈顶元素对应的值为-1
	{
		_iStackTop++;
		if(_iStackTop >= MAXSIZE)
		{
			return;
		}
		else
		{
			_itemArr[_iStackTop] = x;
		}
		if(x > max())
		{
			_iLinkNextMaxItem[_iStackTop] = _iMaxIndex;//采用类似头插法的方式来保存最大元素,使_iLinkNextMaxItem[_iStackTop]中始终保存的是最大值下标
			_iMaxIndex = _iStackTop;
		}
		else
		{
			_iLinkNextMaxItem[_iStackTop] = -1;//如果不是最大值,就始终维持最大值下标为-1
		}
	}
	T pop()
	{
		T ret;
		if(_iStackTop < 0)
		{
			printf("Stack is Empty!");
			return NULL;
			//throw new exception("Stack is Empty!");
		}
		else
		{
			ret = _itemArr[_iStackTop];
			if(_iStackTop == _iMaxIndex)//记住,返回栈顶最大值元素之后,要重新修改链表,更新最大值下标为链表中下一个元素,并使栈顶减减
			{
				_iMaxIndex = _iLinkNextMaxItem[_iStackTop];
			}
			_iStackTop--;
		}
		return ret;
	}
private:
	int _iStackTop;
	int _iMaxIndex;
	int _iLinkNextMaxItem[MAXSIZE];//维护最大值的序列
	T _itemArr[MAXSIZE];//存放数据
};

//用两个栈来实现一个队列
template<typename T>
class Queue
{
public:
	T maxValue(T a,T b)
	{
		return a > b ? a : b;
	}
	bool empty()
	{
		if(stackA.empty() && stackB.empty())
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	T max()
	{
		return maxValue(stackA.max(),stackB.max());//牛逼,用递归来做
	}
	void EnQueue(T x)
	{
		stackB.push(x);
	}
	T DeQueue()
	{
		if(stackA.empty())//如果队列A空了,就将B中的元素全部放入A中
		{
			while(!stackB.empty())//每个元素被移动的次数最多3次:从B堆栈进入,当A堆栈为空时,从B堆栈弹出并压入A堆栈,从A堆栈弹出
			{
				stackA.push(stackB.pop());
			}
		}
		return stackA.pop();//返回A中弹出的栈顶元素
	}
private:
	Stack<T> stackA;//注意标准是stack,queue,小写
	Stack<T> stackB;//注意,这里调用的是我们自己写的栈
};

void queueMax_stack(int* pArr,int iLen)
{
	Stack<int> sta;
	for(int i = 0 ; i < iLen ; i++)
	{
		sta.push(pArr[i]);
	}
	printf("%d\n",sta.max());//关键这里只能返回最大值,对于2 1 3 4这种,只会返回4 3 2, 但是1不会返回是因为已经到达最后的
}

void queueMax_queue(int* pArr,int iLen)
{
	Queue<int> que;
	for(int i = 0 ; i < iLen ; i++)
	{
		que.EnQueue(pArr[i]);
	}
	while(!que.empty())
	{
		printf("%d\n",que.max());//关键这里只能返回最大值,对于2 1 3 4这种,只会返回4 3 2, 但是1不会返回是因为已经到达最后的	
		que.DeQueue();
	}
}

void process()
{
	int n;
	while(EOF != scanf("%d",&n))
	{
		int iArr[MAXSIZE];
		for(int i = 0 ; i < n ; i++)
		{
			scanf("%d",&iArr[i]);
		}
		//queueMax_stack(iArr,n);
		queueMax_queue(iArr,n);
	}
}

int main(int argc,char* argv[])
{
	process();
	getchar();
	return 0;
}

你可能感兴趣的:(编程之美)