堆排序:
①堆的逻辑是一颗完全二叉树;
②它使用的是顺序存储(也就是数组);
③它的作用:一般都是用于找最值。
堆排序的过程:
1、建堆
2、对建好的堆进行向下调整。
(因为建堆是自底向上的且序列位于无序状态,所以建好堆以后要进行向下调整)
但是为什么排升序要建大堆呢?
排升序的话,使用大堆是非常方便的,我们每次向下调整都可以得到剩余数据的最大值,即堆顶元素。然后放到最后,使用分治的思想,每调整一次,要排序的数据就少一个。当交换到最后一个结点时,数组已经排好序了。
而如果用最小堆的话,此时堆顶的元素是最小的,当我们取出堆顶元素时,此时小根堆的性质就变了,那么下次就找不到第二小的元素了,还要重新建堆。所以不能使用小堆排升序
排升序代码如下:
#include<stdio.h>
int h[100];//用来存堆
int n;//用来表示堆的大小
void swap(int x, int y) {//后面的调整函数要用到交换
int t = h[x];
h[x] = h[y];
h[y] = t;
return;
}
void siftdown(int i) {//向下调整函数,创建最大堆
int flag = 0;//用来判断需不需要调整
int t;//用来记录最大的值的下标
while (i*2 <= n && flag==0) {//如果存在左孩子,并且需要调整
//先与左孩子比较
if (h[i] <= h[2 * i])
t = 2 * i;
else t = i;
//如果右孩子存在,再与右孩子比较
if (i * 2 + 1 <= n) {
if (h[t] < h[2 * i + 1])
t = 2 * i + 1;
}
if (t != i) {//说明自己不是最大的,就要交换
swap(i, t);
i = t;//更新i为刚才与它交换的儿子的结点编号,,便于接下来继续向下调整
}
else flag = 1;//说明这个结点比它的两个孩子都大,就不用再交换了
}
}
void creat() {//创建堆,从最后一个非叶结点到第一个结点依次进行向下调整
int i;
for (i = n / 2; i >= 1; i--) {
siftdown(i);
}
return;
}
//排序函数
void heapsort() {
while (n > 1) {
swap(1, n);//最大元素与最后一个元素,这样就保证了最大的元素一直在后边
n--;//已经排好了最大的元素,就不用再调整它了,所以直接将堆的元素个数减少1
siftdown(1);//再让第一个元素向下调整以保持最大堆的特性
}
return;
}
int main(void) {
scanf_s("%d", &n);
int i,num;
num = n;
for (i = 1; i <= n; i++)
scanf_s("%d", &h[i]);
creat();
heapsort();
for (i = 1; i <= num; i++)
printf("%d ", h[i]);
return 0;
}