数据结构---堆排序

本文详细介绍了堆排序的概念,包括完全二叉树、满二叉树、大顶堆和小顶堆的定义。堆排序是一种排序算法,通过构建大顶堆或小顶堆来实现元素的排序。具体步骤包括将数据构建成堆、交换根节点与末尾节点并调整堆、重复此过程直至排序完成。此外,还给出了堆排序的代码实现,包括`HeapAdjust`和`HeapSort`两个关键函数,以及最终的排序结果展示。
摘要由CSDN通过智能技术生成

1、叶子完全二叉树:节点必须按从左到右存放,少也要从右边节点开始少,不能从中间突然少一个节点

满二叉树:也是完全二叉树的特例

大顶堆:父节点的值大于孩子节点                               小顶堆:父节点的值小于孩子节点

                                                 

 叶子节点:

2、堆排序:默认从小到大排序,使用大顶堆

                    默认从大到小,使用小顶堆

(1)、先将数组中的数据想象成一个完全二叉树的结构 

 (2)将其调整为大顶堆,观察调整完毕的大顶堆,可得到根节点的值一定是所有值中最大值,现在已将找到了最大值,则可以将最大值与最后一个值的进行位置交换,最后一个位置记为最大值,相当于完成了一趟冒泡排序

        整规则:从最后一个非叶子节点开始调整(从右向左、从下到上),将其作为临时根节点去调整(非叶子节点一共有三个13 、12、21,最后一个非叶子节点是13)

先调整红色框,再橙色框,最后绿色框(依据从右向左、从下到上)

红色框调整:

步骤:(i)先将根节点的值取出来保存到tmp,

           (ii)需要找到当前空白节点的较大的孩子节点

           (iii)较大孩子节点和tmp比较,如果比tmp大,则将较大的孩子节点向上移动

           (iv)孩子节点移动后,不能直接将tmp插入倒原先较大孩子的位置上,因为它可能还有孩子节点的值比起大

           (v)那么tmp什么时候插入?

                    a:触底(较大的孩子节点没有孩子节点)

                    b:较大孩子节点的孩子节点值都小于tmp

(3)将最大值和最后一个节点的值进行交换,然后将尾节点剔除,不参与下一次排序

(4)重读(2)、(3),直到有序节点只剩一个,则结束。

        现在,如果继续调整为大顶堆,则不需要从内到外调整,只许将最大的那个框调整一次即可,因为只有一根节点为初始节点的框受到影响,可能不稳定,其他框不受影响。

 例如,一次调整之后如上图,只有绿色不稳定,其他框依旧保持大顶堆状态。

调整流程图: 

 

3、父节点位置为i,左右孩子节点的位置下标为2i+1,2i+2

     子节点位置为i,父节点的位置下标为   (i-1)/2  取整

堆排序代码:

//一次调整函数时间复杂度O(log2 n),空间复杂度
void HeapAdjust(int arr[],int start,int end)
{
	//assert
	int tmp = arr[start];
	for (int i = start * 2 + 1; i <= end;i = start * 2+1)//start*2+1相当于start节点的左孩子
	{
		if (i<end && arr[i + 1]>arr[i])//i<end代表存在右孩子,且右孩子的值大于左孩子
		{
			i++;//此时i指向右孩子
		}
		//此时i指向较大的孩子
		if (arr[i] > tmp)
		{
			arr[start] = arr[i];
			start = i;//更改start的位置,i指向新的子孩子位置,体现在for循环语句3
		}
		else
		{
			break;//退出for循环,触发情况2
		}
	}
	arr[start] = tmp;
}
//堆排序,时间复杂度O(n*log2 n),空间复杂度O(1),不稳定
void HeapSort(int* arr, int len)
{
	//1、整体从最后一个非叶子节点由内到外调整
	//首先需要知道最后一个非叶子节点的下标,
	for (int i = (len - 1-1) / 2; i >= 0; i--)//因为最后一个非叶子结点是最后一个叶子结点的父节点,最后一个叶子节点的下标为len-1
	{
		
		HeapAdjust(arr, i,len-1);//调用一次调整函数,i为一次调整的父节点下标,这里第三个参数没有规律,则直接给最大值
	}
	//此时,已经调整为大顶堆

	//此时,当前根节点的值和当前最后一个节点的值进行交换,最后将当前尾节点剔除
	for (int i = 0; i < len - 1; i++)
	{
		int tmp = arr[0];
		arr[0] = arr[len - 1 - i];//当前根节点的值和当前最后一个节点的值进行交换
		arr[len - 1 - i] = tmp;

		HeapAdjust(arr, 0, (len - 1 - i) - 1);//此时只需要再次调用一次调整为大顶堆的一次函数.len-1-i是当前的尾节点,再给其-1,相当于提出当前最后一个尾节点
	}
}

void Show(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[] = { 12,2,39,88,4,6,25,232,62,221 };
	
	HeapSort(arr, sizeof(arr) / sizeof(arr[0]));
	Show(arr, sizeof(arr) / sizeof(arr[0]));

	return 0;
}

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值