数据结构初阶——堆排序

news2024/9/20 16:33:07

思维导图:

目录

一,堆排序的概念

二,堆排序的实现

2.1将数组变成堆  

2.2堆有序化

二,全部代码

一,堆排序的概念

     百度百科的解释如下:堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。 

     也就是说,堆排序是一种利用堆这种数据结构来对数据进行排序的算法。

二,堆排序的实现

想要使用堆排序,那我们就要有堆。但是堆是什么呢?堆实际上就是数组。所以我们可以对数组进行堆排序。比如数组:a[10] = { 55,1,88,15,66,10,44,88,7,6 }。如果用满二叉树的结构来表示,那就是:

               

这样子的。但是,这是一个堆吗?其实很容易的看出这不是一个堆。因为如果它是一个堆的话,那它不是大堆就是一个小堆。小堆就要满足父节点要比子节点要小,大堆就要满足父节点要比子节点要大。但是这个堆是一个条件都不满足,所以它不是一个堆。

2.1将数组变成堆  

   我们现在想要让一个数组变成堆,比如数组:a[10] = { 55,1,88,15,66,10,44,88,7,6 }。

堆排序的前提是什么?有堆!我们要对数组进行堆排序的话还需要插入数据建堆吗?不需要了。但是我们要对数组的数据进行调整来让它变成一个大堆或小堆。那我们该怎么做到调整数组的数据来建立一个规规矩矩的大堆或小堆呢?答案是利用向上调整算法。

向上调整算法代码:

void AdjustUp(int* a, int n)//向上调整算法创建小堆
{
	assert(a);
	int child = n;
	int parent = (child-1)/2;//寻找子节点的父节点
	while (child > 0)
	{
		if (a[child] < a[parent])//利用循化不断调整子节点与父节点之间的关系
		{
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

这个代码该怎么运行呢?

以这个不规矩的堆为例:

#绿色数字是下标# 

   当我们使用AdjustUp时:AdjustUp(a,9)。那我们要调整的就是下标为:9->4->1->0这一支。

因为我们要建立的是小堆,所以调整以后我们的堆就会变成:

 可以看到,画上蓝色圈的这一支就被调整完毕了。但是这个堆还不是一个规规矩矩地堆。

所以为了让这个堆变成一个规规矩矩的堆,我们就这样使用堆排序:

for (int i = 0;i < sizeof(a) / sizeof(a[0]);i++)
	{
		AdjustUp(a, i);//每次插入数据都向上调整。
	}

 经过这个操作以后,我们的堆就会变成:

 这就是一个名正言顺的小堆了。但是它仍然不是有序的(升序或降序)

2.2堆有序化

     通过上述向上调整的操作以后,可以看到这个数组已经被调整成一个小堆了。但是它仍然不是有序的。这就没有实现排序的功能。所以为了将这个堆变得有序,我们就要通过另外两个操作:1.交换根节点与最后一个节点     2.向下调整建堆。

1.交换根节点与最后一个节点

代码:

void swap(int* p1, int* p2)//交换函数swap
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

2.向下调整函数

代码:

void AdjustDown(int* a, int n, int parent)
{
	assert(a);
	int child = 2 * parent + 1;//默认孩子节点是左节点
	while (child < n)
	{
		if (a[child] < a[parent])
		{
			if (child + 1 < n && a[child] > a[child + 1])//如果右节点小于左节点那就将左节点变成右节点
			{
				child++;
			}

			swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}

}

但是和向上调整建堆一样,这个代码只能调整一支。并且不能排序。但是通过下面的操作,我们就能排序了。

代码:

int end = sizeof(a) / sizeof(a[0]) - 1;
	while (end > 0)
	{
		swap(&a[0], &a[end]);//交换首尾两个数
		AdjustDown(a, end, 0);//向下调整实现降序的堆排序
		end--;//每次都要调整交换最后一个叶子节点的位置
	}

经过向上调整以后,我们的堆是这样的:

经过第一个swap调整以后变成这样:

这一步操作直接将最小的值放在了最后一个位置。

然后执行向下调整:

 向下调整,调整了画圈的那一支。这个操作的目的就是将次小的数据放在根节点处。

然后end--,交换根节点与end指向的节点的值。将次小的数放在倒数第二的位置:

然后再交换,以此类推将每一个小数据放在后面就会将数组变成一个降序的数组:

 

假如你想要得到一个升序的数组怎么办呢?只要一个操作。把建立的小堆改成大堆就行了。

代码:

先建立大堆:

void AdjustUp(int* a, int n)//向上调整算法创建大堆
{
	assert(a);
	int child = n;
	int parent = (child-1)/2;//寻找子节点的父节点
	while (child > 0)
	{
		if (a[child] > a[parent])//利用循化不断调整子节点与父节点之间的关系
		{
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

大堆:

 向下调整排序变成升序:

void AdjustDown(int* a, int n, int parent)
{
	assert(a);
	int child = 2 * parent + 1;
	while (child < n)
	{
		if (a[child] >a[parent])
		{
			if (child + 1 < n && a[child] < a[child + 1])
			{
				child++;
			}

			swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}

}
while (end > 0)
	{
		swap(&a[0], &a[end]);//交换首尾两个数
		AdjustDown(a, end, 0);//向下调整实现降序的堆排序
		end--;//每次都要调整交换最后一个叶子节点的位置
	}

排序后:

 

 

#注意#:

       这里建大堆变成建小堆的操作的改变就是改变一下父节点与子节点的交换条件----子节点小于父节点时交换节点改为子节点大于父节点时交换节点。排序堆也是将节点的交换条件改变。

#总结# :

      在写完两个排序后可以总结到:

               1.要排升序就要建立大堆。

                2.要排降序就要建小堆。

二,全部代码

排升序:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void swap(int* p1, int* p2)//交换函数swap
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
void AdjustUp(int* a, int n)//向上调整算法创建小堆
{
	assert(a);
	int child = n;
	int parent = (child-1)/2;//寻找子节点的父节点
	while (child > 0)
	{
		if (a[child] > a[parent])//利用循化不断调整子节点与父节点之间的关系
		{
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void AdjustDown(int* a, int n, int parent)
{
	assert(a);
	int child = 2 * parent + 1;
	while (child < n)
	{
		if (a[child] >a[parent])
		{
			if (child + 1 < n && a[child] < a[child + 1])
			{
				child++;
			}

			swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}

}
int main()
{
	int a[10] = { 55,1,88,15,66,10,44,88,7,6 };
	for (int i = 0;i < sizeof(a) / sizeof(a[0]);i++)
	{
		AdjustUp(a, i);//向上调整建堆
	}
	int end = sizeof(a) / sizeof(a[0]) - 1;
	while (end > 0)
	{
		swap(&a[0], &a[end]);//交换首尾两个数
		AdjustDown(a, end, 0);//向下调整实现降序的堆排序
		end--;//每次都要调整交换最后一个叶子节点的位置
	}
	return 0;
}

排降序:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void swap(int* p1, int* p2)//交换函数swap
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
void AdjustUp(int* a, int n)//向上调整算法创建小堆
{
	assert(a);
	int child = n;
	int parent = (child-1)/2;//寻找子节点的父节点
	while (child > 0)
	{
		if (a[child] < a[parent])//利用循化不断调整子节点与父节点之间的关系
		{
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void AdjustDown(int* a, int n, int parent)
{
	assert(a);
	int child = 2 * parent + 1;
	while (child < n)
	{
		if (a[child] <a[parent])
		{
			if (child + 1 < n && a[child] > a[child + 1])
			{
				child++;
			}

			swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}

}
int main()
{
	int a[10] = { 55,1,88,15,66,10,44,88,7,6 };
	for (int i = 0;i < sizeof(a) / sizeof(a[0]);i++)
	{
		AdjustUp(a, i);//向上调整建堆
	}
	int end = sizeof(a) / sizeof(a[0]) - 1;
	while (end > 0)
	{
		swap(&a[0], &a[end]);//交换首尾两个数
		AdjustDown(a, end, 0);//向下调整实现降序的堆排序
		end--;//每次都要调整交换最后一个叶子节点的位置
	}
	return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/565125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python——第7章 pandas数据分析实战

7.1pandas常用数据类型 7.1.1一维数组与常用操作 import pandas as pd import matplotlib.pyplot as plt#设置输出结果对齐方式 pd.set_option(display.unicode.ambiguous_as_wide,True) pd.set_option(display.unicode.east_asian_width,True)#自动创建从0开始的非负整数索引…

优化器| SGD/Adam/

前言&#xff1a;最近准备复习一下深度学习的基础知识&#xff0c;开个专栏记录自己的学习笔记 各种SGD和Adam优化器整理 基本概念 优化&#xff1a;最大化或最小化目标函数&#xff0c;具体指最小化代价函数或损失函数 损失函数 J(θ)f(hθ(x)&#xff0c;y)&#xff0c;h…

RK3568平台开发系列讲解(项目篇)TensorFlow图像分类

🚀返回专栏总目录 文章目录 一、安装tensorflow环境二、图像分类2.1、准备数据集2.2、构建和训练模型2.3、测试模型2.4、TensorFlow Lite模型2.5、模型转换和模拟测试三、部署推理测试沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 TensorFlow 是一个基于数据流编程…

Python机器学习入门 - - 贝叶斯算法学习笔记

文章目录 前言一、贝叶斯算法简介二、贝叶斯算法的数学原理1. 条件概率2. 全概率公式3. 贝叶斯公式4. 朴素贝叶斯分类器5. 高斯朴素贝叶斯分类器和伯努利朴素贝叶斯分类器 三、Python实现朴素贝叶斯分类总结 前言 贝叶斯公式是我们高中就耳熟能详的统计概率定理&#xff0c;贝…

UnityVR--ResourceManager--资源管理

目录 简介 加载资源的几种方式 资源加载的管理器Resload.cs ResLoad类的应用举例 简介 这里记录一个资源管理工具集&#xff0c;提供一些方法将一些Object、Prefab直接从Assets文件夹中加载到场景中。 加载资源的几种方式 在项目中我们经常需要使用一些随时取用的东西&…

2023 华为 Datacom-HCIE 题库 06--含解析

多项选择 1.[试题编号&#xff1a;190185] &#xff08;多选题&#xff09;如图所示&#xff0c;PE 1和PE2之间通过Loopback0接口建立MP-BGP邻居关系&#xff0c;在配置完成之后&#xff0c;发现CE1和CE2之间无法互相学习路由&#xff0c;以下哪些项会导致该问题出现? A、PE1…

GDB调试工具

GDB&#xff08;GNU Debugger&#xff09;是一个功能强大的命令行调试工具&#xff0c;用于调试 C、C 程序以及其他编程语言的程序。它是 GNU 项目的一部分&#xff0c;可在多个操作系统上使用&#xff0c;包括 Linux、macOS 和 Windows&#xff08;通过 MinGW 或 Cygwin&#…

针对KF状态估计的电力系统虚假数据注入攻击研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

如何用ChatGPT学Python

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 ChatGPT的能力大家肯定都听说过&#xff0c;很多学生应该都亲身体验过。它在自然语言处理方面的出色表现绝对颠覆了之前公众对人…

一文详解Java自定义注解

目录 简介 JDK注解 Target Retention Documented Inherited 第三方注解 自定义注解 举例 默认字符串注解 实现指定包名称扫描注解 简介 注解&#xff08;Annotation&#xff09;是Java SE 5.0 版本开始引入的概念&#xff0c;它是对 Java 源代码的说明&#xff0c;…

FreeRTOS中断配置和临界值

Cortx-M 中断 优先级分组 Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级&#xff0c;因此STM32把指定中断优先级的寄存器位减少到4位。抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。高的抢占式优先级可以打断低的抢占式优先级&am…

mysql加强小结 203446

数据库三范式: 什么是范式 规则:想要设计一个好的关系,必须要满足一定的约束条件,有几个等级,一级比一级高 ​ 解决什么问题:让数据库设计更加简洁,结构更加清晰,否则容易造成数据冗余 数据库有哪些范式? ​ 数据库有七大范式,常用的只有三个范式 **第一范式:**业务上属…

电磁兼容(EMC)基础(二)

目录 1.1 什么是电磁兼容&#xff08;EMC&#xff09; 1.2 各种各样的“干扰” 1.3 电磁兼容三要素 1.4 什么是分贝 1.5 天线 1.1 什么是电磁兼容&#xff08;EMC&#xff09; 电磁兼容(Electro Magnetic Compatibility&#xff0c;EMC)是电子、电气设备或系统的一种重要…

面试题基础篇

文章目录 1、二分查找2、冒泡排序3、选择排序4、插入排序5、希尔排序6、快速排序7、ArrayList8、Iterator9、LinkedList10、HashMap10.1、基本数据结构底层数据结构&#xff0c;1.7和1.8有什么不同&#xff1f; 10.2、树化与退化为何要用红黑树&#xff0c;为何一上来不树化&am…

【开放原子训练营(第三季)inBuilder低代码开发实验室学习心得】

今天要给大家介绍的项目是UBML 什么是UBML呢&#xff1f; UBML&#xff08;统一业务建模语言 Unified-Business-Modeling-Language&#xff09;是一种用于快速构建应用软件的低代码开发建模语言&#xff0c;是开放原子开源基金会&#xff08;OpenAtom Foundation&#xff09;…

数据结构与算法:树形查找

一.二叉排序树&#xff08;BST&#xff09; 1.个人理解 左子树结点值 < 根结点值 < 右子树结点值对二叉排序树进行中序遍历&#xff0c;可以得到一个递增的有序数列 2.二叉树查找 原理&#xff1a; 对于一个给定的二叉排序树&#xff0c;如果要查找一个节点&#xff0…

并发知识杂谈

在JAVA语言层面&#xff0c;怎么保证线程安全&#xff1f; 有序性&#xff1a;使用happens-before原则 可见性&#xff1a;可以使用 volatile 关键字来保证&#xff0c;不仅如此&#xff0c;volatile 还能起到禁止指令重排的作用&#xff1b;另外&#xff0c; synchronized 和…

进程和编码

一、python代码的运行方式 1.脚本式 2. 交互式 一般用于代码的测试 二、进制及相互之间的转换 1. 进制 2.进制之间相互转换 在python中&#xff0c;十进制是以整形的形式存在&#xff0c;其他进制是已字符串的形式存在。 二进制/八进制/十六进制都可与十进制相互转换。但…

走向编程大师之路的几个里程碑

走向编程大师之路的几个里程碑 1语言关 2算法关 3系统关 4 编译器关 如下的系统的核心代码都有一万行以上&#xff0c;是规模和复杂度足够 大&#xff0c;可以检验开发者的模块化编程能力&#xff0c;掌控复杂度的能力。 使用什么编程语言本身是不重要的&#xff0c;能够有能…

常用消息中间件简介

一、 分布式系统消息通信技术简介 分布式系统消息通信技术主要包括以下几种&#xff1a; 1. RPC(Remote Procedure Call Protocol). 一般是C/S方式&#xff0c;同步的&#xff0c;跨语言跨平台&#xff0c;面向过程 2. CORBA(Common Object Request Broker Architecture). CO…