数据结构——树(3)——堆的建立与排序

二叉堆的建立

在继续学下去之前,我们先来看看我们学习堆这个数据结构到底有什么意义,或者说有什么用。
- 排序(堆排序,heap sort)
- Google map(即在两个地点之前寻找最短的路径)
- 所有的优先队列问题
- 事件的模拟
- Huffman 编码(霍夫曼编码)

那么我们在上次提到过一个问题,我们所有的操作都是建立在一个已经建好的堆中,但是现实中,我们怎么可能那么轻松就得到一个建好的堆呢,所以我们还是得自己在一堆毫无规律的数组中,自己建立一个二叉堆。
那么构建堆的最好方式是什么呢?(也就是我们怎么写一个builtHeap()的方法)
假设我们要把下面的一组数构建成二叉堆:
这里写图片描述
我们需要把它们一个一个插入堆中(假设一开始有个空堆),我们上篇讲过,我们往堆中插入一个元素的算法复杂度为O(logN),那么我们插入N个元素呢?当然就是O(n log n)了!
下面我们就先说说具体的步骤:
1. 按原始顺序将所有元素插入(构建)二叉树,复杂度为(O(n))
2. 从树的最底层开始,寻找第一个带有孩子的节点(例如在 n/2处),对每个元素实行 down-heap操作。调整整个树。
3. 也就是说:

for (int i=heapSize/2;i>0;i--){
    downHeap(i);
}

老规矩,来波图文解释:
1. 首先,我们按顺序写出数组,并将他们构建成一个二叉树,如图:
这里写图片描述
2. 接着我们寻找我们的最底层的,含有孩子的节点,也就是43,再看看我们for循环,此时,heapSize的值为9,int(9/2) = 4.如下图:
这里写图片描述
3. 然后我们在for循环中执行down-heap操作!(数据结构——树(2)——二叉堆的基本操作原理
这里写图片描述
这里写图片描述
4. 重复上述步骤,直到我们的堆顶完成后,我们在检查右边是否符合:
这里写图片描述
5. 最后我们就建立了我们的小堆,这个过程实际就是一个插入元素的过程,复杂度就是O(n)
这里写图片描述

堆排序(heap sort)

好了,现在再来谈谈堆排序。假设我们现在已经从上述的步骤中建立了一个完整的堆,那么我们可以通过以下的步骤来实现一个堆的排序。
- 首先,heapify一个数组(即在未排序的数组上调用build-heap,建立一个堆)
- 其次,遍历整个数组并执行dequeue()操作,但注意此时不是返回最小元素,而是与最后一个元素进行交换
- 循环完成后,数组将从低到高排序。

有点不理解吗?图文说话:
1. 假设我们给定一个未成堆的数组,如下所示:
这里写图片描述
这里写图片描述
2. 执行我们的builtHeap()操作,如下所示:
这里写图片描述
这里写图片描述
3. 循环遍历堆,并调用dequeue操作,将根元素与堆末尾的元素交换,heapSize()相应减一:
这里写图片描述
这里写图片描述
4. 此时按照dequeue()原理,根应该变为了5,heapSize由于减一,相应的堆末尾变成了22,如下图:
这里写图片描述
这里写图片描述
5. 执行3的操作,变成:
这里写图片描述
6. 一直这样,直到heapSzie为0的时候,堆排序完成。(绿色部分代表已经完成的排序)
这里写图片描述
7. 此时这就是一个已经排好序的堆,算法复杂度为O(nlog n)。

有机会我会用C++将整个过程写出来。

猜你喜欢

转载自blog.csdn.net/redrnt/article/details/79129512