编程之美-3.7-队列中取最大值操作问题
1. 简述
假设有这样一个拥有3个操作的队列:
1. EnQueue(v):将v加入队列中
2. DeQueue:使队列中的对首元素删除并返回此元素
3. MaxElement:返回队列中的最大元素
请设计一个数据结构和算法,让MaxElement操作的时间复杂度尽可能的低。
2. 思路
起初没仔细看,还以为与此前的自定义栈-pop-push-min-时间复杂度都为O(1) 是一样的,后来才发现不是一回事,有差别的。对于栈来说,我们可以的入栈和出栈不会影响辅助数组内的情况,假设当前N个元素,(为了说明简单,下标从1开始),辅助空间的F[1]记录的是A[1,1]内的最值位置,F[2]记录的是A[1,2]内的最值位置,···,F[N]记录的是A[1,N]内的最值位置。在插入F[k+1]=A[F[k]]与A[k+1]两者最值的位置,插入复杂度为O(1),在删除第k+1个节点时,删除A[k+1]和F[k+1],这并不影响F[1]-F[k],因此删除的复杂度为O(1),取最值,假设当前N个元素,即返回A[F[N]]。
对于队列来说,如果套用栈的辅助数组方法,假设当前有N个元素,下标从1开始,那么F[1]记录的是A[1,N]中的最值,F[2]记录的是A[2,N]中的最值,···,F[N]记录的是A[N,N]中的最值。当A[k+1]入队时,需要更新F[1]到F[k],F[K+1]=K+1,因此插入的复杂度为O(N),当F[k]出队时,删除F[k]即可(每次出队的都是第一个元素,实际上此时F[1]-F[k-1]已经出队完毕了),因此删除的复杂度为O(1)。取最值的复杂度是O(1)。
队列与栈的区别很清楚了。编程之美上给了两个答案,一个是构建最大堆,另一个是用两个栈来实现。
最大堆的方法:
队列本身要么顺序结构要么链接结构,还那么存。另外对于队列每个元素构建一个节点(包含在队列中的位置),这些节点构成一个最大堆,因此插入和删除操作都要维护这个最大堆,时间复杂度都是O(LogN),取最大值的复杂度为 O(1)。
两个栈的方法:
A栈,B栈,这两个栈都是前面提到的pop-push-min复杂度都为O(1)的空间换时间的实现。
取最值:返回A栈的最值和B栈的最值相比后的最值。复杂度O(1)。
入队操作:直接入到B栈中。复杂度O(1)。
出队操作:如果A栈不为空,直接A栈出栈,复杂度为O(1),如果A栈为空,那么将B栈内容逐个出栈并且逐个入栈到A中,然后A栈出栈,复杂度O(N),实际上是B栈的长度。
对于这种方法,如果对列的操作时,一连串的入栈,然后是一连串的出栈,那么就是首先不停向B入栈,然后第一个出栈,B栈元素全压入A栈,A出栈一个,这一步是N的复杂度,但是此后是不停的从A出栈,这都是O (1)的复杂度。还不错呢。而且借助了栈的代码,方便实现。对于这样的情景,就是只有第一个出栈的时候,要O(N),复杂度不是很均匀。对于每个元素来说,要么入B栈,入A栈,从A栈弹出,即总体是3N,平均下来基本上是O(3),要不最大堆的O(LogN)是快了不少呢。
3. 参考
编程之美,3.7,队列中取最大值操作问题