问题引入
选择排序法每次从未排序对象中选择最小值,插入已排序对象的后端,主要问题在大量未排序对象中寻找最小值,数量过大则影响速度。如果可以加快最小值寻找速度,则可提高选择排序法排序速度。基于此,Heap排序法让搜寻的路径由树根至最后一个树叶,而不是整个未排序部份,从而加快最小值寻找速度。
问题分析
Heap排序法使用Heap Tree(堆积树),树是非线性结构,堆积树Heap Tree是一个二叉树。堆积树每一个双亲节点值若小于子节点,则称为最小堆积(Min Heap)。每一个双亲节点值若大于子节点,则称之为最大堆积(Max Heap),同一层的子节点不在意其大小关系,也即类似于“小顶堆和大顶堆”特点。
例如,下图为一个堆积树:
使用一维数组储存堆积树的所有元素及对应顺序,为方便计算,设置起始索引为1,相当于树根位置。如果左子节点储存在阵列中的索引为 i,则其双亲节点的索引为 i/2,而右子节点为 i+1,(不太清楚的伙伴可以复习一下 树 相关概念哦)。例如上图,存储在一维数组后为:
那么,如何建立堆积树?也就是创建“小顶堆”的过程(不太清楚的伙伴可以复习一下 堆 相关概念哦)。例如下图创建最小堆积树,则每次检查双亲节点和子节点,若双亲节点值是否小于子节点值,将小的元素不断与双亲节点交换,直到满足堆积树的条件为止(条件:双亲结点值必定小于子节点值)。
建立好堆积树之后,树根一定是整个树的最小值。
我们的目的是: 取出最小值(树根),同时剩下的元素继续按上述步骤转换为最小堆积树,之后仍是取出最小值(树根),不断重复直至无元素。如下图1、图2所示:
图1
图2
重复上述步骤,因为使用一维数组储存堆积树元素及对应位置,每次均是取最小值,因此,最后得到的一维数组就是已排序好的状态。在整个过程中,和选择排序算法主要区别在于,使用堆积树方式查找最小值速度更快。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 10
#define SWAP(x,y) {int t; t = x; x = y; y = t;} //元素交换
//函数声明
void createheap(int[]);
void heapsort(int[]);
int main(){
int number[MAX+1] = {-1};
int i, num;
srand(time(NULL)); //刷新每次产生的随机数,若无此句,每次随机数相同,亲自尝试一下便知
printf("排序前:");
for(i = 1; i <= MAX; i++){
number[i] = rand() % 100; //产生0-99的随机数,这里没有采用固定的数组值进行排序,随机生成
printf("%d ", number[i]);
}
printf("\n建立堆积树:");
createheap(number);
for(i = 1; i <= MAX; i++) printf("%d ", number[i]);
printf("\n");
heapsort(number);
printf("\n");
return 0;
}
void createheap(int number[]){
int i, s, p;
int heap[MAX+1] = {-1};
for(i = 1; i <= MAX; i++){
heap[i] = number[i];
s = i;
p = i / 2;
while(s >= 2 && heap[p] > heap[s]){
//交换元素顺序,也可将交换语句写在此处
SWAP(heap[p], heap[s]);
s = p;
p = s / 2;
}
}
for(i = 1; i <= MAX; i++) number[i] = heap[i];
}
void heapsort(int number[]){
int i, m, p, s;
m = MAX;
while(m > 1){
//交换元素顺序,也可将交换语句写在此处
SWAP(number[1], number[m]);
m--;
p = 1;
s = 2 * p;
while(s <= m){
if(s < m && number[s+1] < number[s]) s++;
if(number[p] <= number[s]) break;
//交换元素顺序,也可将交换语句写在此处
SWAP(number[p], number[s]);
p = s;
s = 2 * p;
}
printf("\n排序中:");
for(i = MAX; i > 0; i--) printf("%d ", number[i]);
}
}