题目:设计一种数据结构和算法,让取队列最大值的操作的事件复杂度降到最低。对队列操作后依然能够简便取出最大值。
对于栈来讲,Push和Pop均是在栈顶完成的,所以很容维护最大值,而且他的时间复杂度是O(1),基本实现如下所示:
1.栈类:
public class MyStack
{
private int _capacity;
private int[] _stackItems;
private int _maxStackItemIndex;
private int _stackTop;
private int[] _link2NextMaxitem;
public MyStack(int capacity)
{
_capacity = capacity;
_stackTop = -1;
_maxStackItemIndex = -1;
_link2NextMaxitem = new int[_capacity];
_stackItems = new int[_capacity];
}
public void Push(int item) {//压栈
_stackTop++;
if (_stackTop >= _capacity)
{
return false;//越界了
}
else
{
_stackItems[_stackTop] = item;
//维护列表,经典, 无论堆栈如何变化_link2NextMaxItem[..._link2NextMaxItem[_link2NextMaxItem[_maxStackItemIndex]]]如此环环相扣的数据保存模式保证了“当时”注意这里是“当时的”第二大数的索引总是可以通过当前的_link2NextMaxItem[_maxStackItemIndex]来获得(同时发生的事情是最大的项被弹出,也就是pop函数里面发生的事情,第二大数成了最大的了),_stackItems[_maxStackItemIndex]则是一直保存着最大项,
if (item > Max())
{ _link2NextMaxitem[_stackTop] = _maxStackItemIndex;//以下两句顺序不能变,思考一下童鞋
_maxStackItemIndex = _stackTop;
} else _link2NextMaxitem[_stackTop] = -1;
}
}
public int Pop() {//弹出元素
int rt = 0;
if (_stackTop < 0)
throw new Exception("Queue is null");
rt = _stackItems[_stackTop];
//维护列表
if (_stackTop == _maxStackItemIndex)
{
_maxStackItemIndex = _link2NextMaxitem[_stackTop];
}
_stackTop--;
return rt;
}
public int Max() //取最大
{
if (_maxStackItemIndex >= 0)
return _stackItems[_maxStackItemIndex];
else
return -1;
}
public bool IsEmpty()
{
return _stackTop == -1;
}
}
联系上面思想,用两堆栈实现一个队列:队列入队的实现:利用栈B入栈所有元素;队列出队的实现:将栈B中的元素放入栈A,对栈A进行出栈操作正好满足队列元素的出队顺序要求
2.队列类(使用了1中定义的堆栈类作为队列的底层数据结构来实现),按照流程画个图就明白了:
/// 队列类
public class MyQueue
{
//利用堆栈与队列的相似性,用两个堆栈来实现队列
private MyStack _stacka;
private MyStack _stackb;
public MyQueue(int capacity)
{
_stacka = new MyStack(capacity);
_stackb = new MyStack(capacity);
}
public void Enqueue(int item)
{
_stackb.Push(item);
}
public int Dequeue()
{
if (_stacka.IsEmpty())
{
while (!_stackb.IsEmpty())
_stacka.Push(_stackb.Pop());
}
return _stacka.Pop();
}
public int Max()
{
return Math.Max(_stacka.Max(), _stackb.Max());
}
}