堆
一、堆的定义
堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
结构性质:堆总是一棵完全树。
堆序性:堆中某个节点的值总是不大于或不小于其父节点的值;
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的意义就在于:最快的找到最大/最小值,在堆结构中插入一个值重新构造堆结构,取走最大/最小值后重新构造堆结构,其时间复杂度为O(logN),而其他方法最少为O(N)
二、堆的表示
堆结构是一种数组对象,它可以被视为一棵完全二叉树。则A[i]存储二叉树中编号为i的结点值(1≤i≤size),树的根为A[1],并且利用完全二叉树的性质,我们很容易求第i个结点的父结点(parent(i))、左孩子结点(left(i))、右孩子结点(right(i))的下标了,分别为:trunc(i/2)、2i、2i+1;
三、基本操作(以最大堆为例)
3.1 insert:向堆中插入一个新元素;
由于需要维持完全二叉树的形态,需要先将要插入的结点x放在最底层的最右边,插入后满足完全二叉树的特点;然后把x依次向上调整到合适位置满足堆的性质。时间:O(logn)。 “结点上浮”“上滤”
例如下图中插入80,先将80放在最后,然后两次上浮到合适位置.
3.2、delete:删除堆顶元素;
操作原理是:当删除节点的数值时,原来的位置就会出现一个孔,填充这个孔的方法就是,把最后的叶子的值赋给该孔并下调到合适位置,最后把该叶子删除。
如图中要删除72,先用堆中最后一个元素来35替换72,再将35下沉到合适位置,最后将叶子节点删除。
“结点下沉”“下滤”
3.3、build:建立一个空堆;
方法1:插入法:
从空堆开始,依次插入每一个结点,直到所有的结点全部插入到堆为止。
时间:O(n*log(n))
方法2:调整法:
序列对应一个完全二叉树;从最后一个分支结点(n div 2)开始,到根(1)为止,依次对每个分支结点进行调整(下沉),以便形成以每个分支结点为根的堆,当最后对树根结点进行调整后,整个树就变成了一个堆。
时间:O(n)
对如图的序列,要使其成为堆,我们从最后一个分支结点(10/2),其值为72开始,依次对每个分支节点53,18,36 45进行调整(下沉).
四、应用
4.1、堆排序
设有n 个元素,欲将其按关键字排序。可以首先将这n个元素按关键字建成堆,将堆顶元素输出,得到n个元素中关键字最大(或最小)的元素。然后,再将剩下的n-1个元素重新建成堆,再输出堆顶元素,得到n个元素中关键字次大(或次小)的元素。如此反复执行,直到最后只剩一个元素,则可以得到一个有序序列,这个排序过程称之为堆排序。
4.2、优先队列(事件模拟)
银行系统模拟
【问题描述】假设银行有四个服务窗口,初始每个窗口都可以提供服务;模拟开始后,每个窗口都能给出最早空闲时间(即最早可以提供服务的时间);当顾客到达后,总可以看到每个窗口标出的最早可以提供服务的时间,由此来选择窗口,其中,顾客按照一定的频率到达;到达后系统随机产生服务时间,同时产生下一个到达事件的时间。当顾客离开窗口时,该窗口为空闲,即可以为下一个等待顾客提供服务。所有到达事件和离开事件都被插入优先队列。 银行系统模拟中的关键部分为客户事件,包括到达事件和离开事件,所有事件都被加上时间戳,放到优先队列中,队列中优先级最高的事件就是时间戳最早的事件。银行模拟中用随机数来决定下个顾客何时到达及当前顾客所需的服务时间,它可保证事件在某一范围内等概率发生。如果,当前到达事件发生在T时刻,下一个到达事件将发生在范围T+arrivalLow和T+arrivelHigh之间。而每个顾客的服务事件在范围ServiceLow和ServiceHigh之间。到达/离开事件数据均应包括:时间、事件类型、顾客号、服务窗口、等待时间、服务时间等。试编写程序模拟银行系统的运行。
五、d叉堆
d叉堆与二叉堆很类似,但其中的每个非叶节叉堆与叉堆很类似,但其中的每个非叶节点有d个子女,而不是两个.
参考: