堆排序详解:

本文介绍了二叉树的基本概念,包括根、父、子节点和堆结构(大根堆和小根堆),并详细阐述了堆排序的节点下标推导方法。堆排序算法的时间复杂度为O(nlogn),空间复杂度为O(1),涉及堆的构建和调整过程以及排序过程中的交换操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概念铺垫:

  • 二叉树  根  父  子   左孩子    右孩子    叶节点    树的深度(也叫层高)
  • 二叉树:左孩子,右孩子,最多只有两个孩子的就是二叉树;
  • m叉树;
  • 所有的树都可以转成二叉树,所有的森林都可以转成树(如何转后面讲树会详细介绍).所以二叉树很重要;
  • 大根堆:所有的节点的值都大于子节点
  • 小根堆:所有的父都小于子

节点下标推导:

  1. 已知父亲节点 i :左孩子:i*2+1 右孩子: i*2+2
  2. 已知孩子节点 i,求父亲节点:(i-1)/2

堆排序的算法思想:

  1. 首先从最大下标所在的子树开始排大根堆,让后从后往前依次处理子树成为大根堆,(第一次完全乱序的情况下处理大根堆是需要多次调整的,但是之后的调整在第一次大根堆基础上,更为便利)
  2. 当完成第一次大根堆排序后,使得最大值处于根节点的位置:arr[0]
  3. 将根节点最大值(arr[0])与当前排序序列的最后一个元素交换arr[len-1],下次排序则可以排除最后一个元素,向前递进,直至交换完成

最后节点下标为i,则开始需要处理的子树的根节点下标为:(i-1)/2

数组长度为 len,则开始需要处理的子树的根节点下标为:(len-1-1)/2

i=len-1;

堆排序的代码实现:

//这是配置好的模板文件
#include <iostream>
#include <string>
using namespace std; 

void HeadAdjust(int* arr, int begin, int end)
{
	int tmp = arr[begin];//子树的根值
	for (int i = 2 * begin + 1; i <= end;i=i*2+1)
	{
		if (arr[i] < arr[i + 1] &&i<end)
		{
			i++;//使得该子树的孩子节点中最大值为i下标的值
		}
		if (arr[i] > tmp)
		{
			arr[begin]=arr[i];
			begin = i;
		}
		else
			break;
		arr[begin]=tmp;
	}

}

void Heapsort(int* arr, int len)
{
	for (int i = (len - 2) / 2; i >= 0; i--)//第一次排序大根堆,i为开始的子树根下标
	{
		HeadAdjust(arr,i,len-1);//每次给予开始和结束下标,此函数处理交换排序
	}

	for (int i = 0; i < len - 1; i++)
		//循环处理每次完成大根堆排序后的arr[0]交换arr[len-i],n个数完成n-1次交换
	{
		int tmp = 0;
		tmp = arr[0];
		arr[0] = arr[len - 1 - i];
		arr[len - 1 - i] = tmp;
		HeadAdjust(arr, 0, len - 1-i-1);
		//这一次在大根堆的基础上,只需要完成一次大根堆排序即可,只有根节点的数值有可能不合适
		//这一次将根节点的数值交换到合适位置即可,所以不用像第一次一样循环子树处理
	}
}
int main()
{
	int arr[] = { 1,3,12,5,4,23,7,2,8 ,7,11,22,31 };
	int len = sizeof(arr) / sizeof(arr[0]);
	Heapsort(arr, len);
	for (int i = 0; i < len; i++)
	{
		printf("%d  ", arr[i]);
	}
	return 0;
}

堆排序的特性:

  1. 时间复杂度:O(nlogn)

        第一次大根堆排序推导时间复杂度为O(n)

之后每次循环N次排序大根堆,每次调用函数O(logn),乘积为:O(n*logn)

合并后忽略了序数为:O(nlogn)

  1. 空间复杂度:O(1),仅交换
  2. 稳定性:不稳定
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值