优先队列
队列是一个操作受限的线性表,数据只能在一端进入,另一端出来,具有先进先出的性质。有时在队列中需要处理优先级的情况,即后面进入的数据需要提前出来,这里就需要优先队列。优先队列是至少能够提供插入和删除最小值这两种操作的数据结构。对应于队列的操作,插入相当于入队,删除最小相当于出队。
链表,二叉查找树,都可以提供插入和删除最小这两种操作。对于链表的实现,插入需要O(1),删除最小需要遍历链表,故需要O(N)。对于二叉查找树,这两种操作都需要O(logN);而且随着不停的删除最小的操作,二叉查找树会变得非常不平衡;同时使用二叉查找树有些浪费,因此很多操作根本不需要。一种较好的实现优先队列的方式是二叉堆(下面简称堆)。
堆
堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。首先堆是完全二叉树(只有最下面的两层结点度能够小于2,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树),其次任意节点的左右孩子(若有)值都不小于其父亲,这是小根堆,即最小的永远在上面。相反的是大根堆,即大的在上面。
因为完全二叉树有很好的规律,因此可以只用数据来存储数据而不需要链表。如下图:
如图保存在数组中的数据A~J,对于任意位置i,其左儿子在数组中位置为2i,其右儿子在数组中位置为2i+1,其父亲位置在floor(i/2)。
堆的插入:插入堆,那么堆中元素将加一,会在最后一个叶子后添加一个叶子,如上图中的J后面。添加了一个叶子后,需要按照大小比较将这个值放到特定的位置,直到满足堆条件,这是个上滤过程。
堆的删除最小:删除堆中最小的一个,对于小根堆来说就是删除根,删除根后,需要将下面的元素按照大小规则往上移,直到删除最后一个叶子,这是个下滤过程。
建堆的时间复杂度为O(N),删除堆最小值的时间复杂度为O(logN),N为节点数。
定义一个堆结构并实现数据插入和删除最小值操作: