堆排序(C语言版)

news2024/12/25 23:44:49

一.堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1. 建堆
升序:建大堆
降序:建小堆
2. 利用堆删除思想来进行排序

1.1.利用上下调整法实现堆排序

第一步:建堆

好了,每次建堆都要问自己下,要建的是什么堆?大堆还是小堆呢?

我们这里就一一来实现,先建大堆

void Print(int* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void Swap(int* p1, int* p2)
{
	int* tmp = *p1;
	* p1=*p2;
	*p2 = tmp;
}
void Adjustup(int* arr2, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr2[child]>arr2[parent])//注意我们是建的大堆
		{
			Swap(&arr2[child], &arr2[parent]);//传地址才能改变值
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void Heapsort(int* arr, int n)
{
	//建立堆
	//问题是:你是建大堆还是小堆?
	//我们这里要建大堆
	for (int i = 1; i < n; i++)
	{
		Adjustup(arr, i);//利用向上调整法
	}
	Print(arr,n);
}

如果你实现过堆的代码相信上面的代码对你来说绝对小菜一碟

由于我们直接调用了打印函数,那么我们来看看结果吧。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void Print(int* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void Swap(int* p1, int* p2)
{
	int* tmp = *p1;
	* p1=*p2;
	*p2 = tmp;
}
void Adjustup(int* arr2, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr2[child]>arr2[parent])//注意我们是建的大堆
		{
			Swap(&arr2[child], &arr2[parent]);//传地址才能改变值
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void Heapsort(int* arr, int n)
{
	//建立堆
	//问题是:你是建大堆还是小堆?
	//我们这里要建大堆
	for (int i = 1; i < n; i++)
	{
		Adjustup(arr, i);//利用向上调整法
	}
	Print(arr,n);
}
int main()
{
	int arr[] = { 4,6,2,1,5,8,2,9 };
	int sz = sizeof(arr) / sizeof(int);
	Heapsort(arr, sz);
	return 0;
}

结果:

这个是不是就满足了大堆

第二步:如何实现排序呢?(别看上面是从大到小排好的,这只是一个巧合,我们要学会正确的排序法)

如果你对此不清楚,那么我要开始表演了。

如果你想,我们是大堆,这说明最大的数即是堆顶,如果我交换数组首尾位置,然后这是不是不再是一颗完全二叉树了,那么如果我再通过向下调整法来排好,重复操作,是不是就会得到一个从小到大的数组,那么不就排好序了,想到这,相信你肯定联想到了堆的删除操作,下面就让我们利用堆的删除来实现它吧!


void Swap(int* p1, int* p2)
{
	int* tmp = *p1;
	* p1=*p2;
	*p2 = tmp;
}
void Adjustdown(int* arr3, int n, int parent)
{
	int child = parent * 2 + 1;//假设左孩子大
	//注意我们建的是大堆
	while (child < n)
	{
		if ((child + 1) < n && (arr3[child] < arr3[child + 1]))
		{
			child++;
		}
		if (arr3[parent] < arr3[child])
		{
			Swap(&arr3[parent], &arr3[child]);//交换父子位置
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}


//堆建好了,现在实现第二步:堆删除
int end = n - 1;
while (end > 0)
{
	//堆删除分以下几步:
	//第一步:首尾元素互换
	Swap(&arr[0], &arr[end]);
	//第二步:向下调整法调整树根
	Adjustdown(arr, end, 0);
	//第三步:删除堆尾
	end--;
}

这对于学过实现堆的你来说依然so easy。

那么就让我们整体检查代码的正确性:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void Print(int* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void Swap(int* p1, int* p2)
{
	int* tmp = *p1;
	* p1=*p2;
	*p2 = tmp;
}
void Adjustup(int* arr2, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr2[child]>arr2[parent])//注意我们是建的大堆
		{
			Swap(&arr2[child], &arr2[parent]);//传地址才能改变值
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void Adjustdown(int* arr3, int n, int parent)
{
	int child = parent * 2 + 1;//假设左孩子大
	//注意我们建的是大堆
	while (child < n)
	{
		if ((child + 1) < n && (arr3[child] < arr3[child + 1]))
		{
			child++;
		}
		if (arr3[parent] < arr3[child])
		{
			Swap(&arr3[parent], &arr3[child]);//交换父子位置
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void Heapsort(int* arr, int n)
{
	//建立堆
	//问题是:你是建大堆还是小堆?
	//我们这里要建大堆
	for (int i = 1; i < n; i++)
	{
		Adjustup(arr, i);//利用向上调整法
	}
	Print(arr,n);
	//堆建好了,现在实现第二步:堆删除
	int end = n - 1;
	while (end > 0)
	{
		//堆删除分以下几步:
		//第一步:首尾元素互换
		Swap(&arr[0], &arr[end]);
		//第二步:向下调整法调整树根
		Adjustdown(arr, end, 0);
		//第三步:删除堆尾
		end--;
	}
	Print(arr, n);
}
int main()
{
	int arr[] = { 4,6,2,1,5,8,2,9 };
	int sz = sizeof(arr) / sizeof(int);
	Heapsort(arr, sz);
	return 0;
}

结果:

如果你是要从大到小排序,操作如下:

建小堆-》堆的尾删

整体而言有三处改动:

1.在void Adjustup(int* arr2, int child)函数中这个语句要改变符号,因为是建小堆了。
        if (arr2[child]>arr2[parent])//
2.在void Adjustdown(int* arr3, int n, int parent)这个函数中这两处符号也要改变,原因是因为你现在是小堆,向下调整法肯定要调整
        if (arr3[child] < arr3[child + 1]))
        if (arr3[parent] < arr3[child])
 

整体如下:

//表示原来的语句

//arr2[child]>arr2[parent]
arr2[child]<arr2[parent]

//if ((child + 1) < n && (arr3[child] < arr3[child + 1]))
if ((child + 1) < n && (arr3[child] > arr3[child + 1]))


//if (arr3[parent] < arr3[child])
if (arr3[parent] > arr3[child])

改完之后结果如下:

现在我们就要对这个算法进行分析:

时间复杂度:建堆为O(NlogN)+选数O(N-1logN)

得出结果:O(NlogN)(非常牛逼的算法)

1.2.只利用向下调整法实现堆排序

大家看上面的代码是不是感觉一个排序要写这么麻烦好不方便啊,是的,我们其实可以只通过一个向下调整就可以实现堆排序,下面看看我的表演吧!

步骤还是和上面一样,其实就改变了建堆的过程,我们现在是通过向下调整法建堆。

看代码:

for (int i = ; i ; i)
{
	Adjustdown(arr,,);
}

我们就是要把上面的空缺填好,那么该如何写呢?

我要告诉你一个概念:向下调整建堆是从第一个非叶子节点开始调整,我们肯定要调整到0

所以我们可以这样写:

for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
	Adjustdown(arr,n,i);
}

其他部分不用改变,所以整体代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void Print(int* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void Swap(int* p1, int* p2)
{
	int* tmp = *p1;
	* p1=*p2;
	*p2 = tmp;
}
void Adjustdown(int* arr3, int n, int parent)
{
	int child = parent * 2 + 1;//假设左孩子大
	//注意我们建的是大堆
	while (child < n)
	{
		if ((child + 1) < n && (arr3[child] < arr3[child + 1]))
		{
			child++;
		}
		if (arr3[parent] < arr3[child])
		{
			Swap(&arr3[parent], &arr3[child]);//交换父子位置
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void Heapsort(int* arr, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		Adjustdown(arr,n,i);
	}
	Print(arr, n);
	//堆建好了,现在实现第二步:堆删除
	int end = n - 1;
	while (end > 0)
	{
		//堆删除分以下几步:
		//第一步:首尾元素互换
		Swap(&arr[0], &arr[end]);
		//第二步:向下调整法调整树根
		Adjustdown(arr, end, 0);
		//第三步:删除堆尾
		end--;
	}
	Print(arr, n);
}
int main()
{
	int arr[] = { 4,6,2,1,5,8,2,9 };
	int sz = sizeof(arr) / sizeof(int);
	Heapsort(arr, sz);
	return 0;
}

结果:

来我们该讨论该算法时间复杂度情况了

建堆O(N)+选数O(NlogN)

时间复杂度:O(N*logN)

注意:该写法不仅简单(比上面那种),而且效率也比上面的高。

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

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

相关文章

STM32G030F6P6读写flash失败问题(HAL)

STM32G030是F0系列的升级版&#xff0c;其在性能上比F0要好很多&#xff0c;具体G0参数如下&#xff1a; 最开始做项目选用的单片机是STM32F030F4P6&#xff0c;但是在后期使用中发现&#xff0c;我的FLASH&#xff08;16K&#xff09;不够用了&#xff0c;就选择了STM32G030F6…

计算机网络——基础知识汇总(八)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

每日算法打卡:递归实现排列型枚举 day 2

文章目录 原题链接题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 题目分析示例代码 原题链接 94. 递归实现排列型枚举 题目难度&#xff1a;简单 题目描述 把 1 ∼ n 1 \sim n 1∼n 这 n n n 个整数排成一行后随机打乱顺序&#xff0c;输出…

CISSP 第1章:实现安全治理的原则和策略

作者&#xff1a;nothinghappend 链接&#xff1a;https://zhuanlan.zhihu.com/p/669881930 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 CIA CIA 三性&#xff1a; 机密性&#xff1a;和数据泄露有关。完整性…

57.网游逆向分析与插件开发-游戏增加自动化助手接口-接管游戏的自动药水设定功能

内容来源于&#xff1a;易道云信息技术研究院VIP课 码云地址&#xff08;master分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;51307d6bf69f2f3c645c70d09f841f5e32da79b9 代码下载地址&#xff0c;在 SRO_EX 目录下&…

开源数据可视化分析工具DataEase本地部署并实现远程访问

目录 前言 1. 安装DataEase 2. 本地访问测试 3. 安装 cpolar内网穿透软件 4. 配置DataEase公网访问地址 5. 公网远程访问Data Ease 6. 固定Data Ease公网地址 结语 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊开源数据可视化分析工…

如何更好的进行API管理

相信无论是前端&#xff0c;还是后端的测试和开发人员&#xff0c;都遇到过这样的困难。不同工具之间数据一致性非常困难、低效。多个系统之间数据不一致&#xff0c;导致协作低效、频繁出问题&#xff0c;开发测试人员痛苦不堪。 API管理的难点在哪&#xff1f; 开发人员在 …

深度学习——PIL和OpenCV

PIL 官方文档 格式互转 opencv cv2.imread() 参数&#xff1a; filepath&#xff1a;读入imge的完整路径 flags&#xff1a;标志位&#xff0c;{cv2.IMREAD_COLOR&#xff0c;cv2.IMREAD_GRAYSCALE&#xff0c;cv2.IMREAD_UNCHANGED} cv2.IMREAD_COLOR&#xff1a;默认参数&…

【华为机试】2023年真题B卷(python)-计算礼品发放的最小分组数目

一、题目 题目描述&#xff1a; 又到了一年的末尾&#xff0c;项目组让小明负责新年晚会的小礼品发放工作。 为使得参加晚会的同时所获得的小礼品价值相对平衡&#xff0c;需要把小礼品根据价格进行分组&#xff0c;但每组最多只能包括两件小礼品&#xff0c;并且每个分组的价格…

Python入门第09篇(conda虚拟环境)

前言 一开始默认安装了最新的Python3.12&#xff0c;搞的倒也顺手&#xff0c;看别人会有不兼容的问题&#xff0c;在我这开始没出现。不过坑总会踩到的&#xff0c;这不就出问题了。pip install一个包一直不行&#xff0c;问了下度娘&#xff0c;说由于这个包使用了一些新技术…

007、控制流

先看下本篇学习内容&#xff1a; 通过条件来执行 或 重复执行某些代码 是大部分编程语言的基础组成部分。在Rust中用来控制程序执行流的结构主要就是 if表达式 与 循环表达式。 1. if表达式 if表达式允许我们根据条件执行不同的代码分支。我们提供一个条件&#xff0c;并且做出…

JavaWeb小项目练习(基于三层架构实现登录,对表增,删,改,查的操作)

一 创建项目并修改项目结构 File->New->Project新建一个名为javaweb01的工程 1 2 javaweb01->New->Module新建一个名为MyManage的模块 1 2 将MyManage模块设置为支持web项目 1 2 将web项目重命名并移动到main路径下 配置pom文件(文件依赖,打包方式,以及支持加载…

24年上半年想考软考,小白如何备考?

软考科目的考试时间&#xff1a; 软考考试时间&#xff1a; 软考高级有三科&#xff08;机考&#xff09;&#xff1a;《综合知识》、《案例分析》、《论文》 软考中级、初级&#xff08;机考&#xff09;&#xff1a; 《基础知识》、《应用技术》 考试报名官网&#xff1a;中…

SpringBoot集成 Websocket 实现服务与客户端进行消息发送和接收

介绍 WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。 效果 客户端效果 服务端日志 pom依赖 <!-- websocket --> <dependency><groupId>org.…

【Matlab】LSTM长短期记忆神经网络时序预测算法(附代码)

资源下载&#xff1a; https://download.csdn.net/download/vvoennvv/88688439 一&#xff0c;概述 LSTM&#xff08;Long Short-Term Memory&#xff09;是一种常用的循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;结构&#xff0c;由于其对于…

1207. 大臣的旅费(dfs求树的直径/图论)

题目&#xff1a; 1207. 大臣的旅费 - AcWing题库 思路&#xff1a; dfs求树的直径。 代码&#xff1a; #include<iostream> #include<cstdio> #include<vector> using namespace std; const int N100100; struct Edge//边的id以及长度 {int id,w; };ve…

Mysql 下载与安装教程(详细介绍与总结)

一&#xff1a;版本介绍 首先&#xff0c;我们需要先进入官网进行下载&#xff0c;在官网中有好几个版本&#xff0c;那么这里我分别简述一下MySQL各个版本区别&#xff1a; 1&#xff1a;企业版&#xff0c;MySQL Enterprise Edition 需要付费的&#xff0c;可以免费试用30天…

redis的搭建及应用(五)-布隆过滤器插件

redis布隆过滤器 可以把布隆过滤器理解为bitmap结构&#xff0c;判断某个对象是否存在时&#xff0c;它可能会误判。但是布隆过滤器也不是特别不精确&#xff0c;只要参数设置得合理&#xff0c;它的精确度也可以控制得相对足够精确&#xff0c;只会有小小的误判概率。 总得来说…

从 0 到 1 实现 ReentrantLock

虽然本文的标题是从 0 到 1 实现 ReentrantLock &#xff0c;但是为了方便理解&#xff0c;我们先从一个问题出发&#xff1a;既然系统已经有 synchronized 关键字了&#xff0c;那么为什么还会出现 ReentrantLock 这种代码层面的锁? 这就要先回顾一下历史了&#xff1a;在 J…

微服务整合:构建高效灵活的分布式系统

随着软件开发的不断演进和业务的复杂性增加&#xff0c;微服务架构已经成为一种流行的解决方案。然而&#xff0c;当涉及到多个微服务之间的整合时&#xff0c;我们需要谨慎考虑如何实现高效、灵活的分布式系统。 微服务架构的流行使得软件开发变得更加灵活和可扩展。然而&…