【数据结构排序算法(七)】堆排序

基础数据结构之八大排序算法(七)

⑦堆排序

时间复杂度:O(nlogn) 其中的循环与平方有关,则时间复杂度为O(nlongn)
空间复杂度:O(1) 问题规模与其额外辅助变量无关
稳定性:不稳定 存在跳跃交换

(以顺序表作为待排序对象)

1.在学习堆排序前需要先了解一些关于树的基本知识

(1)树:
在这里插入图片描述
(2)二叉树:

【每个节点最多只能分出两个节点
在这里插入图片描述

(3)叶子节点:

【树最外层的节点】
在这里插入图片描述
(4)完全二叉树:

在这里插入图片描述
(5)满二叉树:

【每一层的节点都满了】
在这里插入图片描述
(6)大顶堆

【父节点的值大于孩子节点的值】
{ 大顶堆的根节点的值一定是所有节点里最大的 }
在这里插入图片描述

(7)小顶堆:

【父节点的值小于孩子节点的值】
{ 大顶堆的根节点的值一定是所有节点里最小的 }
在这里插入图片描述
2.基本思想

以数组arr[]={21,7,8,1,12,9,66}为例:

【1】将所有数组臆想成一个完全二叉树:
在这里插入图片描述
【2】将这个完全二叉树化为一个大顶堆(从小到大排序)
在这里插入图片描述
具体调整方法:

{ 1.将根节点的值保存一份 }
{ 2.找当前节点的左右孩子中较大的孩子 }
{ 3.这个较大的孩子和根节点比较,如果大于根节点则向上走 }
{ 4.原先较大孩子的节点不可能还有大于根节点的节点 }
{ 5.不能直接插入根节点,需要继续向下判断 }
{ 6.那什么时候插入(1.触底了 2.左右孩子都小于根节点)}

(先调整最后一个非叶子节点)
在这里插入图片描述
(从右向左调整第二个非叶子节点)

在这里插入图片描述
(从下向上调整第三个非叶子节点)

在这里插入图片描述
(此时已经成为一个大顶堆)

【3】将大顶堆的根节点的值和最后一个节点交换,并将交换后的最后一个节点剔除,循环这个过程,直到只剩下一个节点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3.代码实现

<1>.所需头文件和宏替换:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<stdbool.h>

#define ar arr[]={21,7,8,1,12,9,66}
#define ELEM_TYPE int

int ar;

<2>.顺序表的建立:

typedef struct Sqlist
{
	ELEM_TYPE* data;
	int length;
	int SIZE;
}Sqlist,*PSqlist;

<3>.顺序表的初始化:

void Init_Sqlist(PSqlist L)
{
	L->data = arr;
	L->length = 0;
	L->SIZE = sizeof(arr) / sizeof(arr[0]);
}

<4>.打印

void Show(PSqlist L)
{
	for (int i = L->length; i < L->SIZE; i++)
	{
		printf("%d ", L->data[i]);
	}
	printf("\n\n");
}

<5>.每一次排序的调整:

void HeapAdjust(PSqlist L, int start, int end)
{
	int tmp = L->data[start];

	for (int i = start * 2 + 1; i <= end; i = start * 2 + 1)
	{
		if (i<end && L->data[i]<L->data[i + 1])  // i < L->SIZE-1 代表右孩子存在,i来保存较大的那个孩子的下标
		{
			i++;
		}

		if (L->data[i] > tmp)
		{
			L->data[start] = L->data[i];//如果孩子的值大于起始的值,则起始的位置放入孩子的值
			start = i;  //因为L->data[i]向上放,所以L->data[i]变为了空白格子,让start指向空白格子。
		}
		else
		{
			break;
		}
	}
	//此时无论是通过for循环结束退出的(触底,没有孩子节点了退出),或是通过break推退出的(左右孩子都小于tmp)退出的
	//将tmp插入到start空白格子处
	arr[start] = tmp;
}

<6>.堆排序:

void HeapSort(PSqlist L)
{
	//将全部数据看成一个完全二叉树,再将这个完全二叉树化为一个‘大顶堆’(从小到大排)
	for (int i = ((L->SIZE - 1 - 1) / 2); i >= 0; i--)      //L->SIZE-1为最后一个数据的下标,(L->SIZE-1-1)/2 为最后一个‘非叶子节点’的下标
	{
		HeapAdjust(L, i, L->SIZE - 1);
	}

	//根节点的值和最后一个节点的值交换,再将最后一个元素剔除,再继续将剩下的数据排成大顶堆,重复这三步
	for (int i = 0; i < L->SIZE - 1; i++)
	{
		int tmp = L->data[0];
		L->data[0] = L->data[L->SIZE-1-i];
		L->data[L->SIZE - 1-i] = tmp;

		HeapAdjust(L, 0, ((L->SIZE - 1 - i)-1));//L->SIZE-1-i是最后一个元素的下标,再减1就将最后一个数据剔除
	}
}

<7>.主函数:

int main()
{
	Sqlist head;
	Init_Sqlist(&head);
	printf("初始数据为:");
	Show(&head);

	HeapSort(&head);
	printf("堆排序后的数据为:");
	Show(&head);
	return 0;
}

<8>.运行结果:

在这里插入图片描述
希望这篇博客能帮助大家更好的学习和理解二路归并排序
感谢阅读!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逐梦的白鹰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值