一、堆排序
堆排序(英语:Heapsort)是指利用堆(Heap)这种数据结构所设计的一种排序算法。
- 把整个数组排列成为一个最大堆,根结点的值就是整个数组的最大值。
- 把根结点的值存入一个临时位置,然后将根结点删除,把最后一个终端结点移到根结点的位置。
- 此时,数组空出了最后一位。将存入临时位置的“最大值”存入数组的最后一位,最后一位完成排序。
- 除最后一位外的数组重新排成一个最大堆,找出“次最大值”,通过临时位置存入数组的倒数第二位,此时,后两位完成排序。
- 如此循环,直到数组成为一个由小到大排列的有序数组
下图为百度百科上堆排序词条上的演示。
堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父结点。
二,什么是完全二叉树
二叉树:
在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
- 二叉树是递归定义的,其结点有左右子树之分
- 二叉树的结点个数可以为 0(空二叉树)。
- 二叉树可以只有左子树或只有右子树,结点的最大度数为 2 。
二叉树类型
- 满二叉树——二叉树的结点数达到最大结点数,即每一层都是满的。叶子结点都在同一深度,其他结点的度都为 2 。
- 完全二叉树——除最后一层外,其余层都已被填满,并且最后一层只会空出右边。
- 平衡二叉树——它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
三,二叉堆
堆(数据结构)
堆就是用数组实现的二叉树,所有它没有使用父指针或者子指针。堆根据“堆属性”来排序,“堆属性”决定了树中节点的位置。
n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4…n/2)。
堆是非线性数据结构,相当于一维数组,有两个直接后继。
- 最大堆:也称大根堆。最大堆每个结点的值都不大于其父结点的值,根结点的值最大。
- 最小堆:也称小根堆。最小堆每个结点的值都不小于其父结点的值,根结点的值最小。
二叉堆一般用数组来表示。
四,构建最大堆步骤
以整型数组{21,1,18,30,6,51}为例。
int a[]={21,1,18,30,6,51};
把整型数组放在完全二叉树内
详细步骤
- 从最后一个非叶子结点a[2]开始,寻找其子结点。a[2]只有a[5]一个子结点。比较a[2]和a[5]的大小。
- 最大堆中父结点的数值应该比子结点的数值大,a[2]和a[5]不符合上述规定,于是交换a[2]和a[5]。
- 从a[2]向前移到a[1]。寻找其子结点。a[1]有a[3]和a[4]两个子结点。
- 比较a[3]和a[4],a[3]值大。比较a[1]和a[3],不符合最大堆的规定,于是交换a[1]和a[3]。
- 从a[1]向前移到a[0]。寻找其子结点。a[0]有a[1]和a[2]两个子结点。
- 比较a[1]和a[2],a[2]值大。比较a[0]和a[2],不符合最大堆的规定,于是交换a[0]和a[2]。
- 最大堆构建完成。
在循环中插入一个输出函数,每进行一次循环就输出一次a[]。
和上图完全一致。
五,c语言实现,逐语句分析
完全二叉树说明:
- 如果根结点位置为1,对于位于位置为n的结点,其左子结点位置为2n,右子结点位置为2n+1。
- 如果根结点位置为0,对于位于位置为n的结点,其左子结点位置为2n+1,右子结点位置为2n+2。
若完全二叉树共有length个结点 (floor函数:向下取整)
- 若根结点位置为1,最后一个终端结点位置lastleaf=length;那么最后一个非终端结点位置lastsub=floor(lastleaf/2)。
- 若根结点位置为0,最后一个终端结点位置lastleaf=length - 1;那么最后一个非终端结点位置lastsub=floor((lastsub - 1)/2)。
- 根结点位置是奇数的,计算时减去小于这个奇数的最大偶数,按根结点位置为1计算结果。
- 根结点位置是偶数的,计算时减去它本身,按根结点位置为0计算结果
#include<stdio.h>
#include<math.h>
void Heap_Adjust(int* a, int n, int lastleaf)//这是一个把数列按最大堆排序的函数
{
int temp = a[n];//temp记录a[n]的数值。
int L_leaf = 2 * n + 1;//L_leaf是n的左节点的位置。
int i;//i是整型,但在数组a[]内,起到类似指针的作用。
for (i = L_leaf; i <= lastleaf; i = 2 * i + 1)//从i=L_leaf开始,执行for循环,循环完成后i指向a[i]的左子节点。当i超出二叉树范围时,循环结束。
{
if (i < lastleaf && a[i] < a[i + 1])//a[i]是a[n]的左子节点,a[i+1]是a[n]的右子节点
i++;
/*if的作用:使i指向a[n]的左右子节点中数值较大的那个节点*/
if (a[i] > temp)//如果a[i]大于temp的话,不满足最大堆的规则,执行if内代码
{
a[n] = a[i];//执行到此,a[n]和a[i]都是“最大值”
n = i;//使n指向原替换a[n]的子节点
}
/*if的作用:使a[n]的数值变成a[n]和a[n]的左右子节点之中的最大值*/
else
break;//如果a[n]的子节点中最大的也大不过a[n],就跳出循环
}
a[n] = temp;//temp记录原a[n]的数值赋给替换a[n]的子节点
}
void Heap_Sort(int* a, int lenth)
{
int lastleaf = lenth - 1;//lastleaf为最后一个终端节点的位置。
int lastsub = floor((lastleaf - 1) / 2);//lastsub为最后一个非终端节点的位置。
int n,i;
for (n = lastsub; n >= 0; n--)//从lastsub位置开始调整二叉树(执行Heap_Adjust函数),直到根节点的位置。
Heap_Adjust(a, n, lastleaf);
/*此时a[]已经是最大堆*/
for (i = 0; i < lastleaf; i++)
{
int temp = a[0];//temp暂存当前数组最大值
a[0] = a[lastleaf - i];//将当前数组最后一个终端结点放入根节点
a[lastleaf - i] = temp;//最大值随i增大向前排序
Heap_Adjust(a, 0, lastleaf - i - 1);//从根节点到最后一个未排序的位置调整数组
}
}
int main()
{
int a[] = {19,50,1,34,26,93,57,74,52,15,3,48,6,25,65,44,83,10,99,64,17,2};
Heap_Sort(a,22);
return 0;
}
一句一注释可把我牛b坏了~
六,分析过程
现在用一个较长的数组分析程序执行过程。
int a[] = {19,50,1,34,26,93,57,74,52,15,3,48,6,25,65,44,83,10,99,64,17,2};
定义一个简单的函数用来按二叉树的格式输出整型数组。
int Output_BTree_int(int* a,int n)
{
int i, j = 1, k = 0;
for (i = 0; i < n; i++)
{
printf("[%02d]%-4d ", i, *(a + i));
k++;
if (k == j)
{
putchar('\n');
k = 0;
j=j*2;
}
}
puts("\n\n");
}
在Heap_Adjust()和Heap_Sort()中插入一些函数在控制台监测运行过程。
注释掉排序过程。
#include<stdio.h>
#include<math.h>
void Heap_Adjust(int* a, int n, int lastleaf)
{
int temp = a[n];
int L_leaf = 2 * n + 1;
int i;
for (i = L_leaf; i <= lastleaf; i = 2 * i + 1)
{
printf("Heap_Adjust i = %d i指向左节点\n", i);/*插入*/
if (i < lastleaf && a[i] < a[i + 1])
{
i++;
printf("Heap_Adjust i = %d i指向右节点\n", i);/*插入*/
}
if (a[i] > temp)
{
a[n] = a[i];
n = i;
Output_BTree_int(a, 22);/*插入*/
}
else
break;
}
a[n] = temp;
Output_BTree_int(a, 22);/*插入*/
}
void Heap_Sort(int* a, int lenth)
{
int lastleaf = lenth - 1;
int lastsub = floor((lastleaf - 1) / 2);
int n,i;
for (n = lastsub; n >= 0; n--)
{
printf("Heap_Sort n = %d\n", n);/*插入,n为Heap_Adjust的开始位置*/
Heap_Adjust(a, n, lastleaf);
}
/*for (i = 0; i < lastleaf; i++)
{
int temp = a[0];
a[0] = a[lastleaf - i];
a[lastleaf - i] = temp;
Heap_Adjust(a, 0, lastleaf - i - 1);
}*/
}
int main()
{
int a[] = {19,50,1,34,26,93,57,74,52,15,3,48,6,25,65,44,83,10,99,64,17,2};
puts("原始数组");
Output_BTree_int(a, 22);/*插入,输出原始数组*/
Heap_Sort(a,22);
puts("最大堆");
Output_BTree_int(a, 22);/*插入,输出最大堆*/
return 0;
}
运行结果。
Ayakari是一个0基础的编程萌新,二级没过的水平QAQ 多谢各位dalao指教~虽然存在感…