堆排序
健堆原理:
利用一段连续的数组空间实现逻辑上的堆。
主要根据完全二叉树的父子节点间的关系,当根节点从0开始时,其父节点i对应的左孩子是 2 * i + 1, 右孩子是2 * i + 2。 相应的子节点i对应的父节点为 (i - 1)/2
push一个元素
(1)插入元素,在末尾插入,每次向上调整 (大根堆)
void shift_up(int ind) { //从下标i开始向上调整
while(ind && data[(ind - 1) / 2] < data[ind]) {
swap(data[(ind - 1) / 2], data[ind]);
ind = (ind - 1) / 2;
}
return;
}
弹出一个元素(最值)
(2)弹出元素,在首部弹出,然后将最后一个元素交换上来,向下调整,流程是先把根节点和左孩子比较,记录较大的位置,然后将其和右孩子比较,记录最大的值的位置,然后交换根节点和最大的值。
void shift_down(int ind) {
int n = cnt - 1; //最大值
while (ind * 2 + 1 <= n) { //确保其左孩子在范围内
int temp = ind;
if (data[temp] < data[ind * 2 + 1]) temp = ind * 2 + 1;
if (ind * 2 + 2 <= n && data[temp] < data[ind * 2 + 2]) temp = ind * 2 + 2;
//此时temp记录最大值的位置
if (temp == ind) break;
swap(data[temp], data[ind]);
ind = temp;
}
return;
}
堆排序思路:
三部分:
1、向下调整,shift_down(int ind)
2、从n/2位置开始,循环向下调整,建堆
3、交换第一个和最后一个位置,从1位置继续向下调整,
根据数组建立初始大顶堆的过程:从最后一个非叶子节点开始,不断向下调整
然后将每次大顶堆的第一个元素放到数组末尾,继续从根节点向下调整
所以只需要手写一个向下调整的函数。
堆排算法
void HeapSort(int *a, int length) {
for (int i = length / 2 - 1; i >= 0; i--) { //建堆
shift_down(a, i, length);
}
for (int i = length -1; i > 0; i--) { //排序
swap(a[0], a[i]); //每次交换一个数,将最大值放到最后
shift_down(a, 0, i); //继续对0位置向下调整
}
return;
}
向下调整
void shift_down(int *a, int ind, int cnt1) { //cnt1代表堆顶元素个数
int n = cnt1 - 1;
while (ind * 2 + 1 <= n) { //确保不超过堆顶位置
int temp = ind;
if (a[temp] < a[ind * 2 + 1]) temp = ind * 2 + 1;
if (ind * 2 + 2 <= n && a[temp] < a[ind * 2 + 2]) temp = ind * 2 + 2;
if (temp == ind) break;
swap(a[temp], a[ind]);
ind = temp;
}
return;
}