第一次接触堆,是在学习堆排序的时候,整个人都是糊糊的,搞不清楚那些操作到底在做什么。其实,堆本质上就是一颗特殊的完全二叉树。既然是一颗完全二叉树,就可以用数组来存储。它的特殊之处在于:根节点都不小(或者大)于子节点。对于堆的操作,如果违反了这个性质,那么就需要对堆进行调整。而调整堆无外乎两种方法:向上调整siftUp或者向下调整siftDown。下面先来看几个堆的性质:
已知一个内节点(非根节点)的下标是i,那么其父节点是i/2,其左孩子和右孩子分别是2*i和2*i+1。
下面的所有操作都是针对大顶堆而言的。
siftUp:
pre:H[1…,i-1]构成了一个堆,除了最后一个元素H[i]之外,其余元素构成了一个大顶堆
post:H[1,…,i]构成一个堆
siftUp(H,i)
j=i/2
while j>0
if(H[j] < H[i])
交换H[i]和H[j]
I=j
siftDown:
pre:H[i+1,…,n]构成了一个堆,H[i,…,n]除了第一个元素H[i]之外,其余元素构成了一个大顶堆
post:H[i,…,n]构成一个堆
siftDown(H, i)
j=2*i
while j<length[H]
if(j < length[H]-1 && H[j]<H[j+1])
j++
if(H[i] < H[j])
交换H[i]和H[j]
I=j
J=2*i
时间复杂度分析:
求父节点:O(1)
求子节点:O(1)
调整堆(siftUp或者siftDown):O(nlgn)
应用:
堆主要有两个应用:一个是在堆排序中,另一个是构造优先级队列。