优先队列
优先队列数据结构属于计算机科学中最精致的一种
6.1 模型
优先队列允许至少两种操作:插入和删除最小者
6.2 一些简单的实现
优先队列的插入和删除最小者的最坏情形时间均为o(logN),但插入操作实际上将花费平均时间,若无删除操作的干扰,该结构的实现将以线性时间建立一个具有N项的优先队列
6.3 二叉堆
堆是一棵完全二叉树,但却不需要链的存在。因为完全二叉树的规律性,可以将堆的节点存储在数组中,通过父节点和子节点位置的数学关系以建立联系。
一个堆结构将由一个数组和一个代表当前堆的大小的整数组成。
堆的堆序性质指父节点小于子节点,堆的结构性质指完全二叉树结构。
堆的插入:给数组末尾赋值,然后进行上滤操作,最坏情形花费logN的时间。
堆的删除最小元:将数组末尾的元素替换掉首个元素,然后进行下滤操作。
堆的其他操作:
decreaseKey,降低关键字的值,可以将关键字的值降低,然后通过上滤操作,提高该关键字的优先级。
increaseKey,提高关键字的值,可以将关键字的值提高,然后通过下滤操作,降低其优先级。
delete,可以先通过降低关键字的值,提高其优先级,然后采用deleteMIn方法将其删除
buildHeap,构建堆,先将数组放入,再从第一个父节点开始下滤操作,只需要o(N)时间;若是逐个插入则要话费NlogN时间,因为逐个插入需要花费为对一倍的树叶节点进行下滤操作。该操作至多需要2N-2次比较。
6.4 优先队列的应用
选择问题:选择第K个大小
若对输入排序,再取第K个,需要n的平方;若只排序k个,其他逐个比较替换,需要nk;最坏均为n平方。
若对输入堆排序,进行K次deleteMIN,花费n+klogN;若堆排序k个,其余逐个比较并下滤,最后取堆中最小,花费k+(n-k)logk;最坏均为nlogN。
事件模拟
银行排队问题
这个问题主要由两个事件构成,顾客到达和顾客离去。
每个顾客有到达时间和服务服务时间。
排队事件的处理进行不能采用定时扫描方法,应该让时间直接跳到下个事件发生,然后统计并收集数据。
6.5 d-堆
当插入对于删除最小者操作时,使用d-堆比较划算。
若插入操作为m次,删除操作为n次,堆的叉数最佳为max(2,m/n);
6.6 左式堆
左式堆的设计是为了有效的支持合并操作,由于左式堆控制了右路径的长度为logN以内,左式堆的合并操作只需要logN的时间复杂度。
零路径长:任一节点的零路径长定义为该节点到一个不具有两个儿子节点的节点的最短路径的长。
左式堆:对于堆中的每个节点,左儿子的零路径长至少与右儿子的零路径长相等。由此可推论而知,左子树的节点数大于等于右子树的节点数。
左式堆的合并操作:递归地将具有大的根植的堆与具有小的根植的堆的右子堆合并,并根据情况调整树根的零路径长和交换子树。此操作既维护了堆的堆序性,又保证了合并操作的logN复杂度。
6.7 斜堆
由于不再保证对的左式性质,斜堆的单次合并操作时间复杂度为N,然而其摊还操作为logN,等同于左式堆合并复杂度。
6.8 二项队列
由于二项队列最多有logN棵树,因此其合并、deleteMin等操作均为logN,对于插入的平均时间复杂度为常数。
对于二项队列的实现,每个节点的儿子都在一个链表中,而且每个节点都有一个对它的第一个儿子的引用,便于快速找出根的所有子树。二项队列要求各个儿子按照它们的子树的大小排序,便于合并操作的进行。
6.9 标准库中的优先队列
java1.5之后出现了PriorityQueue,由于优先队列有许多实现方法,因此该类库的设计者没有选择让优先队列成为一个接口。
疑难点
1.左式堆的懒惰删除策略:
当执行deleteMin操作时,若堆的根节点已被作删除标志,则对堆进行一遍先序遍历,将含有删除标志的节点删除,并将删除后分割成的两个子堆依次放入队列中。先序遍历完成后,依次读取两个子堆进行合并操作,并将合并后的堆放到队列末尾,不断合并直到只剩一个堆为止。