堆排序(C语言)

news2025/2/25 6:30:43

前言

        在上一篇内容:大小堆的实现(C语言),我们实现了关于创建大小堆的各函数与实现。但是如果突然要使用一个堆排序但是此时并没有一个现成的堆,这就需要花费时间去新建实现堆的插入删除这些操作从而实现一个堆,并且在插入的过程中存在内存空间的消耗(malloc空间),那是否有一些其它办法可以避免以上问题呢?

数组建堆

我们能不能不消耗空间就完成一次建堆?直接将所给的数组建堆可以吗?

这些数字在物理逻辑上是一个数组,而在虚拟逻辑上是一个完全二叉树,那么将这个完全二叉树进行一些调整后是不是就能得到一个堆呢?我们尝试利用之前的向上调整算法,完成建堆的操作:

上述操作的本质就是模拟堆插入的过程建堆

(第一个视为堆,第二个插入然后向上调堆、第一个和第二个视为堆,第三个插入然后向上调堆)

可以发现我们在没有申请内存空间的前提下,仅利用一个向上调整算法就完成了将数组建堆的操作,并最终到了一个小堆,但是如果我们在选出这个小堆中的最小值后,再想要选出这个堆中的次小值就会出现问题:

可以发现当要选出次小值时,缺少了”1“的小堆的剩余元素并不能算作是一个小堆它们都是无序的,所以为了选出次小值,我们还要建堆(将数组元素向上调整建堆)以此类推这就相当于选一次值就要建一次堆,那还不如直接选用时间复杂度为O(n)的暴力遍历数组选取最小值,次小值,而我们建堆的目的就是为了方便我们选出我们想要的最大/小、次大/小的数,但是很明显如果将利用向上调整算法建立一个小堆是无法满足我们的需求的,所以我们应该利用向上调整算法去建立一个大堆。

结论:若利用向上调整算法建小堆,如果取出最小值,那么剩下的数有可能不是堆,故应建大堆 

只需要该算法做一些调整,将(a[child] < a[parent])变为(a[child] > a[parent]),就可以建大堆:

调整后的结果是我们得到了一个大堆,此时即使将堆顶的“9”删除,剩余的两个子树也仍然是大堆的形式,这就意味着我们在选出最小数或次小数后只需要进行向下调整即可不需要考虑重新建堆,当我们想要最小值,只需要交换首尾元素的位置,取出此时的堆顶元素即可,想要获取次小值,只需要将尾部的数不再视为堆的一部分再次重复以上操作即可:

堆排序

其实上述的内容,就是我们实际中堆排序的一种实现方式,即利用向上调整算法建大堆然后再利用向下调整算法将挑选后剩余的元素重新调整为虚拟逻辑上的大堆以便下一次的选取

利用向上/下调整算法将数组中的数字在虚拟逻辑上重新变为大/小堆与重新建堆的区别是?

在堆排序算法中,我们需要构建一个最大/小堆来进行排序。这可以通过两种方式实现:

1. 重新建堆:这是一种直接的方法,从数组的首元素开始逐个插入到空堆中,步骤如下:

  • 创建一个空的堆
  • 将数组中的元素逐个插入到空堆中
  • 最终得到了一个满足最大(或最小)堆性质的完整二叉树

2. 向上/下调整:这是一种当已有部分构成的近似完全二叉树进行调整来达到目标状态的方法

  • 向上调整:从某节点开始,将其与父节点比较并交换位置,直至满足最大/小堆性质。该过程会将当前节点及其祖先节点推向正确位置,并保持子树仍然满足对应性质。
  • 向下调整:从某节点开始,将其与左右子节点比较并交换位置,直至满足最大/小堆性质。该过程会使当前节点及其后代子树变为有序树,并保持其他部分仍然满足对应性质

3、区别:

  1. 重新建堆是一种从零开始构建堆的方法,它将整个数组视为初始状态,并按顺序插入元素来构建最大(或最小)堆。这种方法需要较多的时间和空间复杂度。
  2. 向上调整和向下调整算法是在已有部分近似完全二叉树的基础上进行调整,通过比较节点与其父节点或子节点来达到维护最大(或最小)堆性质的目标。这两种算法可以在不重建完全二叉树的情况下对特定位置进行优化操作,因此具有更高效率。

4、总结:

  1. 如果已拥有一个无序数组并希望将其转换为一个满足最大/小堆性质的完全二叉树,则可以使用重新建堆方法
  2. 如果你已经拥有一个近似完全二叉树,并且只需对其中某些位置进行修正以满足最大(或最小)堆性质,则应使用向上调整或向下调整算法

利用向上调整算法实现堆排序

使用向上调整算法实现堆排序的步骤如下:

  1. 构建最大堆:从数组的第一个非叶子节点开始,依次对每个节点进行向上调整操作,使其满足最大堆性质。可以通过遍历非叶子节点并依次调用向上调整函数来完成这一步骤。

  2. 排序:将根节点(数组首元素)与末尾元素交换位置,并将末尾元素从堆中移除。然后对新的根节点进行一次向下调整操作,以维持最大堆性质。重复这个过程直到所有元素都被移除并排好序。

具体实现代码如下所示(假设数组是从索引 0 开始):

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

//向上调整,此时传递过来的是最后一个孩子的元素下标我们用child表示
void AdjustUP(HPDataType* a,int child)
{
	//由于我们要调整父亲与孩子的位置所以此时也需要父亲元素的下标,而0父亲元素的下标值 = (任意一个孩子的下标值-1)/ 2 
	int parent = (child - 1) / 2;
	//当孩子等于0的时位于树顶(数组首元素的位置),树顶元素没有父亲,循环结束
	while(child > 0)
	{
		//如果孩子还未到顶且它的下标对应的元素值小于它的父亲的下标对应的元素值,就将父子位置交换,交换玩后还要将下标对应的值“向上移动”
		//if (a[child] < a[parent])
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		//由于这是一个小堆,所以当孩子大于等于父亲时不需要交换,直接退出循环即可
		else
		{
			break;
		}
	
	}
}

//向下调整算法
void AdjustDown(HPDataType* a, int size, int parent)
{
	//根据之前的推论,左孩子的下标值为父亲下标值的两倍+1,左孩子的下标值为父亲下标值的两倍+2
	int child = parent * 2 + 1;
	//循环结束的条件是走到叶子结点
	while (child < size)
	{
		//假设左孩子小,若假设失败则更新child,转换为右孩子小,同时保证child的下标不会越界
		//if (child + 1 < size && a[child + 1] < a[child])
		if (child + 1< size && a[child + 1] > a[child])
		{
			++child;
		}

		if (a[child] < a[parent])
		{
			//如果此时满足孩子小于父亲则交换父子位置,同时令父亲的下标变为此时的儿子所在下标,儿子所在下标变为自己的儿子所在的下标(向下递归)
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		//如果父亲小于等于左孩子就证明删除后形成的新堆是一个小堆,不再需要向下调整算法,循环结束
		else
		{
			break;
		}
	}
}

//堆排序
void HeapSort(int* a, int n)
{
	//构建大堆
	for (int i = 1; i < n; i++)
	{
		AdjustUP(a, i);
	}
	
    //排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}

}

int main()
{
	int a[] = { 4,6,2,1,5,8,2,9 };
	HeapSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	//HeapSort(a, i);
	return 0;
}

利用向下调整算法实现堆排序

1、从最后一个结点的父亲开始向下调整

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

//向下调整算法
void AdjustDown(HPDataType* a, int size, int parent)
{
	//根据之前的推论,左孩子的下标值为父亲下标值的两倍+1,左孩子的下标值为父亲下标值的两倍+2
	int child = parent * 2 + 1;
	//循环结束的条件是走到叶子结点
	while (child < size)
	{
		//假设左孩子小,若假设失败则更新child,转换为右孩子小,同时保证child的下标不会越界
		if (child + 1< size && a[child + 1] < a[child])
		{
			++child;
		}

		if (a[child] < a[parent])
		{
			//如果此时满足孩子小于父亲则交换父子位置,同时令父亲的下标变为此时的儿子所在下标,儿子所在下标变为自己的儿子所在的下标(向下递归)
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		//如果父亲小于等于左孩子就证明删除后形成的新堆是一个小堆,不再需要向下调整算法,循环结束
		else
		{
			break;
		}
	}
}


//堆排序
void HeapSort(int* a, int n)
{
    //构建最小堆
	for(int i = (n-1-1)/2;i>=0;--i)
    {
        AdjustDown(a,n,i);
    }
	
    //排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}

}

int main()
{
	int a[] = { 4,6,2,1,5,8,2,9 };
	HeapSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	//HeapSort(a, i);
	return 0;
}

~over~

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

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

相关文章

如何有效进行主数据治理

在企业信息化建设不断推进、逐渐进行数字化转型的今天&#xff0c;几乎所有的企业都卷入到数据及其处理&#xff08;数据收集、存储、检索、传输、分析和表示&#xff09;的浪潮中&#xff0c;数据已成为重要生产要素和无形资产&#xff0c;针对主数据的全生命周期管理迫在眉睫…

老司机带你一课学透,核心分布式事务框架之Alibaba Seata框架经验总结

在现代分布式系统中&#xff0c;保证数据的一致性和可靠性是一项重要的挑战。Alibaba Seata是一款开源的分布式事务解决方案&#xff0c;它提供了强大的支持&#xff0c;帮助开发者处理分布式事务。本文将向您介绍Alibaba Seata框架&#xff0c;并分享一些使用该框架的经验总结…

Java零基础——SpringSecurity篇

1.认证授权的基础概念 1.1 什么是认证&#xff08;登录&#xff09; 进入移动互联网时代&#xff0c;大家每天都在刷手机&#xff0c;常用的软件有微信、支付宝、头条等&#xff0c;下边拿微信来举例子说明认证相关的基本概念&#xff0c;在初次使用微信前需要注册成为微信用户…

31名!美创科技再次入选《中国网络安全企业100强》

12月1日&#xff0c;安全牛联合中国计算机学会抗恶劣环境计算机专业委员会、信息产业信息安全测评中心正式发布第十一版《中国网络安全企业100强》&#xff08;以下简称“100强”&#xff09;。 美创科技再次入选“中国网络安全企业100强”&#xff0c;位列31名&#xff08;数据…

07、基于LunarLander登陆器的强化学习案例(含PYTHON工程)

07、基于LunarLander登陆器的强化学习&#xff08;含PYTHON工程&#xff09; 开始学习机器学习啦&#xff0c;已经把吴恩达的课全部刷完了&#xff0c;现在开始熟悉一下复现代码。全部工程可从最上方链接下载。 基于TENSORFLOW2.10 0、实践背景 gym的LunarLander是一个用于…

无需服务器,无需魔法,拥有一个微信机器人就是这么简单

前情提要 还没看过的朋友可以看一下上一篇文章《拥有一个微信机器人总共需要几步&#xff1f;》在这篇文章里&#xff0c;我们提到&#xff0c;创建微信机器人需要一个大前提--你得有一台服务器。现在&#xff0c;不再需要了&#xff01;没错&#xff0c;上一篇提到的Serverles…

Python爬虫:使用Scrapy框架进行高效爬取

Python爬虫可使用的架构有很多&#xff0c;对于我而言&#xff0c;经常使用Scrapy异步处理框架Twisted&#xff0c;其实意思很明确&#xff0c;Scrapy可以实现多并发处理任务&#xff0c;同一时间将可以处理多个请求并且大大提高工作效率。 Scrapy是一个强大且高效的Python爬虫…

文章阅读——Scaffolding protein functional sites using deep learning

1.最终幻想: 无中生有的蛋白质从头设计 零.导读 近几年&#xff0c;蛋白质结构预测领域连续取得重大突破。首先是【AlphaFold】&#xff0c;在可以充分利用共进化信息结合深度神经网络生成空间约束条件并降低相空间的搜索&#xff0c;极大地帮助了蛋白质的结构建模&#xff0…

扁平按钮样式

上图 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>扁平按钮</title><style>body {margin: 0;padding: 0;height: 100vh;display: flex;justify-content: center;ali…

改造python3中的http.server为简单的文件上传下载服务

改造 修改python3中的http.server.SimpleHTTPRequestHandler&#xff0c;实现简单的文件上传下载服务 simple_http_file_server.py&#xff1a; # !/usr/bin/env python3import datetime import email import html import http.server import io import mimetypes import os …

OpenCV-python:图像像素类型转换与归一化

目录 1.图像像素类型转换 2. 图像像素转换适用情形 3.图像归一化 4.归一化方法支持 5.归一化函数 6.知识笔记 1.图像像素类型转换 图像像素类型转换是指将图像的像素值从一种类型转换为另一种类型。常见的像素类型包括无符号整数类型&#xff08;如8位无符号整数、16位无符…

树_左叶子之和

//给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 // // // // 示例 1&#xff1a; // // // // //输入: root [3,9,20,null,null,15,7] //输出: 24 //解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 24 //…

彩色成像的基础和应用 原理 Principles(一)

下面我将不定期尽可能出一系列&#xff08;我觉的非常好&#xff09;翻译的文章来解释颜色这们学科。【下图为此次翻译的书籍封面】 Introduction: 颜色是一种与光的物理学&#xff0c;物质的化学&#xff0c;物体的几何特性以及人…

电脑回收站还原的文件在哪里找到?如何找回回收站还原的文件

电脑回收站是一种非常有用的功能&#xff0c;可以帮助我们恢复无意中删除的文件。然而&#xff0c;许多人可能不清楚还原的文件在哪里可以找到。本文将为您带来详细解答&#xff0c;并帮助您找回回收站还原的文件。 电脑回收站还原的文件在哪里找到 当我们使用电脑的回收站功…

微信小程序开发平台系统源码 附带完整的搭建教程

随着移动互联网的快速发展&#xff0c;微信小程序作为一种新型的应用形态&#xff0c;凭借其轻量化、易用性等特点&#xff0c;逐渐成为了移动开发领域的新宠。 以下是部分代码示例&#xff1a; 系统特色功能一览&#xff1a; 1.完善的开发工具&#xff1a;本系统提供了一整套…

设计一个在裸机下使用的简单软件定时器(3):功能测试

0 前言 在RTOS中&#xff0c;我们经常用到软件定时器来为我们处理一些对于实时性要求不高的定时任务。在裸机开发中&#xff0c;我们可能也有很多需要定时执行的任务&#xff0c;为了优雅地执行这些定时任务&#xff0c;本文设计一个在裸机下使用的简单软件定时器&#xff0c;…

java基础之HashSet详解

HashSet详解 HashSet是基于HashMap实现的一个单列存储的集合类&#xff0c;将所有的数据存在HashMap的key值中&#xff0c;而value全部使用一个Object对象存储 继承关系 public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable…

Unity | 渡鸦避难所-1 | 修复资源导入后呈现洋红色(Built-in 转 URP)

1 前言 Unity 编辑器导入 Asset Store 的资源包后&#xff0c;在预览和使用时&#xff0c;发现对象显示为洋红色 以小狐狸为例&#xff0c;打开资源包中的场景&#xff0c;可以看到小狐狸和地板均显示为洋红色 这是因为 Asset Store 中的资源包大部分是针对内置渲染管线项目制…

Python代码部署的三种加密方案,其中一种你肯定不知道

文章目录 前言一、代码混淆二、代码打包三、代码编译3.1 pyarmor快速使用3.2 pyarmor进阶使用关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、…

tNavigator 23.2 x64

Rock Flow Dynamics&#xff08;RFD&#xff09;很高兴地宣布发布我们旗舰产品tNavigator的最新版本。版本 23.2 现在可供用户使用。 tNavigator长期以来一直被认为是油藏工程师和地质学家的强大工具&#xff0c;可为复杂的油藏行为提供准确的建模和模拟。最新版本为所有模块带…