概括(不想看后面晦涩的内容的话)(大根堆为例,小根堆同理):
- 拿到一串数,以宽度优先的方式生成一棵二叉树。
- 建堆:从最深的子树开始(从下往上),修改使得每一棵子树的根节点的值为子树中的最大值。(这样一来,这个大根堆的根节点就是整个堆的最大值)
- 排序:pop出堆的根(当前最大),将堆中最后一个数放在堆根处,然后从上往下修改,使得每棵子树的根节点仍然是子树中的最大值。重复这个过程,直到堆为空
堆排序虽然是在数组上进行排序, 但实际上它使用了层次遍历的二叉树的思想。
一、 排序的整体结构
void sort(int num) {
build(num);
for(int i = num-1; i > 0; i--) {
int current = array[i];
array[i] = array[0];
insert(current, 0, i-1);
}
}
构建堆 -> 从后往前将每个数插入到“合适”的位置上
二、主要函数: 插入函数 insert
void insert(int current, int low, int high) {
int large = 2*low+1; //array[large]是array[low]的左孩子
while(large <= high) {
if(large < high && array[large] < array[large+1])
large++; //找到array[low]的较大的那个孩子
if(current > array[large])
break; //后面的值都太小了, 没有必要往下筛选了
array[low] = array[large];
low = large;
large = 2*low+1; //继续筛选, 为更小的分支找到根节点(领袖)
}
array[low] = current; //在正确的位置插入current(current以下的数都比current小)
}
解释:
参数current:要插入的值, low:插入范围的最低点(深度最小), high: 插入范围的最高点(深度最大)
函数实现的功能:
将“以array[low]所在的节点为根,low至high之间的树”当中的最大的节点值换到根节点上,
可以形象地理解为给这个分支找一个领袖
三、 用insert函数建堆
void build(int num) {
for(int i = num/2-1; i >=0; i--)
insert(array[i], i, num-1);
}
num/2-i: 最深节点的父节点
通过在insert函数中的描述,我们可以看出, build的过程就是从底层到顶层一级一级找“领袖”的过程,
当for循环结束时,每一个节点都是以它为根的树当中的最大值了(领袖),这也就是我们所需要的堆
四、回过头来看排序sort函数
void sort(int num) {
build(num);
for(int i = num-1; i > 0; i--) {
int current = array[i];
array[i] = array[0];
insert(current, 0, i-1);
}
}
因为当前任意的节点都是以它为根的树的最大值,所以我们每次使用insert函数,就有一个大的数到了他本应该在的
位置,排序过的树就不再移动,所以参数high是i-1。我们不断地找到“剩余”的树当中的最大值放在最后, for循
环结束后,排序也就结束了。
完整代码
#include <iostream>
using namespace std;
int array[100] = {3,5,4,2,1,8,7,9,6};
void insert(int current, int low, int high) {
int large = 2*low+1;
while(large <= high) {
if(large < high && array[large] < array[large+1])
large++;
if(current > array[large])
break;
array[low] = array[large];
low = large;
large = 2*low+1;
}
array[low] = current;
}
void build(int num) {
for(int i = num/2-1; i >=0; i--)
insert(array[i], i, num-1);
}
void sort(int num) {
build(num);
for(int i = num-1; i > 0; i--) {
int current = array[i];
array[i] = array[0];
insert(current, 0, i-1);
}
}
int main() {
int num;
cin >> num;
for(int i = 0; i < num; i++)
cin >> array[i];
sort(num);
for(int i = 0; i < num; i++)
cout << array[i] << " ";
}
例子
array 3 5 4 2 1 8 7 9 6
build 9 6 8 5 1 4 7 2 3
sort 1 2 3 4 5 6 7 8 9