Aya的学习笔记:数组排序:堆排序,c语言实现,逐语句分析【超详细】

一、堆排序

堆排序(英语:Heapsort)是指利用(Heap)这种数据结构所设计的一种排序算法。

  1. 把整个数组排列成为一个最大堆,根结点的值就是整个数组的最大值。
  2. 把根结点的值存入一个临时位置,然后将根结点删除,把最后一个终端结点移到根结点的位置。
  3. 此时,数组空出了最后一位。将存入临时位置的“最大值”存入数组的最后一位,最后一位完成排序。
  4. 除最后一位外的数组重新排成一个最大堆,找出“次最大值”,通过临时位置存入数组的倒数第二位,此时,后两位完成排序。
  5. 如此循环,直到数组成为一个由小到大排列的有序数组

下图为百度百科上堆排序词条上的演示。
这是百度百科上堆排序词条上的演示
堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父结点。

二,什么是完全二叉树

二叉树:

在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树二叉堆

  • 二叉树是递归定义的,其结点有左右子树之分
  • 二叉树的结点个数可以为 0(空二叉树)。
  • 二叉树可以只有左子树或只有右子树,结点的最大度数为 2 。

二叉树类型

  1. 满二叉树——二叉树的结点数达到最大结点数,即每一层都是满的。叶子结点都在同一深度,其他结点的度都为 2 。满二叉树
  2. 完全二叉树——除最后一层外,其余层都已被填满,并且最后一层只会空出右边。
    来自百度百科
  3. 平衡二叉树——它是一棵空树或它的左右两个子树的高度差的绝对值不超过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};

把整型数组放在完全二叉树内
在这里插入图片描述
详细步骤

  1. 从最后一个非叶子结点a[2]开始,寻找其子结点。a[2]只有a[5]一个子结点。比较a[2]和a[5]的大小。
  2. 最大堆中父结点的数值应该比子结点的数值大,a[2]和a[5]不符合上述规定,于是交换a[2]和a[5]。
  3. 从a[2]向前移到a[1]。寻找其子结点。a[1]有a[3]和a[4]两个子结点。
  4. 比较a[3]和a[4],a[3]值大。比较a[1]和a[3],不符合最大堆的规定,于是交换a[1]和a[3]。
  5. 从a[1]向前移到a[0]。寻找其子结点。a[0]有a[1]和a[2]两个子结点。
  6. 比较a[1]和a[2],a[2]值大。比较a[0]和a[2],不符合最大堆的规定,于是交换a[0]和a[2]。
  7. 最大堆构建完成。

在这里插入图片描述
在循环中插入一个输出函数,每进行一次循环就输出一次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指教~虽然存在感…

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值