堆排序(C语言版)

一.堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:
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.mzph.cn/news/591983.shtml

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

相关文章

MVCC 并发控制原理-源码解析(非常详细)

基础概念 并发事务带来的问题 1&#xff09;脏读&#xff1a;一个事务读取到另一个事务更新但还未提交的数据&#xff0c;如果另一个事务出现回滚或者进一步更新&#xff0c;则会出现问题。 2&#xff09;不可重复读&#xff1a;在一个事务中两次次读取同一个数据时&#xff0c…

【逆向】从逆向角度看C++

从逆向角度看C 1.2.1 虚函数地址表&#xff08;虚表&#xff09; 定义&#xff1a;当类中定义有虚函数时&#xff0c;编译器会把该类中所有虚函数的首地址保存在一张地址表中&#xff0c;即虚函数地址表。虚表信息在编译后被链接到执行文件中&#xff0c;因此所获得的虚表地址…

《3D数学基础-图形和游戏开发》阅读笔记 | 3D数学基础 (学习中)

文章目录 3D数学基础矢量/向量概述 - 什么是向量单位矢量&#xff1a;只关注方向不关注大小 数学运算矢量的加法与减法减法的几何意义计算一个点到另一个点的位移矢量的点积与叉积 矩阵矩阵的几何意义 3D数学基础 矢量/向量 在笔记中 变量使用小写字母表示&#xff0c;a由于…

Linux实用指令篇

目录结构 Linux文件系统结构是从Unix文件结构演进过来的。在Linux文件系统中&#xff0c;通用的目录名用于表示一些常见的功能。 Linux 的文件系统是采用层级式的树状目录结构&#xff0c;在此结构中的最上层是 根目录 “/”&#xff0c;然后在此目录下再创建其他的目录。在L…

安装 Node.js、npm

安装 nodejs 安装Node.js的最简单的方法是通过软件包管理器。 Node.js官网&#xff1a;https://nodejs.org/en/download/ cd /usr/local/src/wget -c https://nodejs.org/dist/v18.16.0/node-v18.16.0-linux-x64.tar.xz xz -d node-v18.16.0-linux-x64.tar.xz tar -xf node…

轻松搞定软件开发:找对软件开发公司的流程与注意事项!

随着数字化时代的来临&#xff0c;软件开发在企业和个人生活中扮演着越来越重要的角色&#xff0c;然而&#xff0c;如何找到一家合适的软件开发公司却成为了一个令人头疼的问题。 本文将为你详细解读找软件开发公司的流程&#xff0c;以及在选择过程中需要注意的事项&#xf…

C#编程-编写和执行C#程序

编写和执行C#程序 可以使用Windows记事本应用程序来编写C#程序。在记事本应用程序中创建C#程序后,您需要编译并执行该程序以获得所需的输出。编译器将程序的源代码转换为机器代码,这样计算机就能理解程序中的指令了。 注释 除了记事本,您还可以使用任何其他文本编辑器来编写…

UnityRenderStreaming使用记录(一)

UnityRenderStreaming 地址https://github.com/Unity-Technologies/UnityRenderStreaming 一、客户端相关 1、unity工程添加Package 2、WebRTC选Version 3.0.0-pre.6&#xff0c;升级会报错 导入Samples 3、打开Broadcast场景 二、服务器相关 这里使用github上的源码&…

kubeadm创建k8s集群

kubeadm来快速的搭建一个k8s集群&#xff1a; 二进制搭建适合大集群&#xff0c;50台以上。 kubeadm更适合中下企业的业务集群。 部署框架 master192.168.10.10dockerkubelet kubeadm kubectl flannelnode1192.168.10.20dockerkubelet kubeadm kubectl flannelnode2192.168.1…

MySQL数据库高级SQL语句及存储过程

目录 一、高级SQL语句 &#xff08;一&#xff09;case语句 1.语法定义 2.示例 &#xff08;二&#xff09;空值(NULL) 和 无值( ) 1.区别 2.示例 &#xff08;1&#xff09;字符长度 &#xff08;2&#xff09;判断方法 ① 空值(NULL) ② 无值( ) &#xff08;3…

centos 7.9 升级系统默认的python2.7到python 2.7.18

centos 7.9 升级系统默认的python2.7到python 2.7.18 备份旧版本 mv /usr/bin/python /usr/bin/python_2.7.5 下载新版本 Download Python | Python.org Python Release Python 2.7.18 | Python.org wget https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tgz cd /…

哨兵1号回波数据(L0级)FDBAQ压缩算法详解

本专栏目录: 全球SAR卫星大盘点与回波数据处理专栏目录-CSDN博客 1. 全球SAR卫星回波数据压缩算法统计 各国的SAR卫星的压缩算法按照时间轴排列如下: 可以看出传统的分块BAQ压缩算法(上图粉色)仍然是主流,哨兵1号其实也有传统的BAQ压缩模式。 本文介绍哨兵1号用的FDBAQ算…

【数据分析】指数移动平均线的直观解释

slavahead 一、介绍 在时间序列分析中&#xff0c;通常需要通过考虑先前的值来了解序列的趋势方向。序列中下一个值的近似可以通过多种方式执行&#xff0c;包括使用简单基线或构建高级机器学习模型。 指数&#xff08;加权&#xff09;移动平均线是这两种方法之间的稳健权衡。…

nginx源码分析-4

这一章内容讲述nginx的模块化。 ngx_module_t&#xff1a;一个结构体&#xff0c;用于描述nginx中的各个模块&#xff0c;其中包括核心模块、HTTP模块、事件模块等。这个结构体包含了一些模块的关键信息和回调函数&#xff0c;以便nginx在运行时能够正确地加载和管理这些模块。…

UE5.1_Gameplay Debugger启用

UE5.1_Gameplay Debugger启用 重点问题&#xff1a; Gamplay Debugger启用不知道&#xff1f; Apostrophe、Tilde键不知道是哪个&#xff1f; Gameplay调试程序 | 虚幻引擎文档 (unrealengine.com) Gameplay Debugger

day02 有序数组的平方 长度最小子数组 螺旋矩阵

题目1&#xff1a;977 有序数组的平方 题目链接&#xff1a;977 有序数组的平方 题意 返回非递减整数数组的每个数字的平方和 也按照递减排序 双指针★ 代码 class Solution { public:vector<int> sortedSquares(vector<int>& nums) {vector<int>…

VMware 虚拟机 ubuntu 20.04 硬盘扩容方法

前言 最近由于需要编译 【RK3568】的 Linux SDK&#xff0c;发现 虚拟机默认的 200G 空间不足了&#xff0c;因此想增加这个 200G 空间的限制&#xff0c;通过网络上查找了一些方法&#xff0c;加上自己亲自验证&#xff0c;确认 硬盘扩容 正常&#xff0c;方法也比较的容易&a…

Vue:Vue与VueComponent的关系图

1.一个重要的内置关系&#xff1a;VueComponent.prototype.proto Vue.prototype 2.为什么要有这个关系&#xff1a;让组件实例对象&#xff08;vc&#xff09;可以访问到 Vue原型上的属性、方法。 案例证明&#xff1a; <!DOCTYPE html> <html lang"en"&…

Java日期和时间(二)

新增的日期和时间 为什么要学习新增的日期和时间 1、代替Calendar LocalDate&#xff1a;年、月、日 LocalTime&#xff1a;时、分、秒 LocalDateTime&#xff1a;年、月、日、时、分、秒 ZoneId&#xff1a;时区 ZoneldDatetime&#xff1a;带时区的时间 2、代替Date Instan…

解决npm,pnpm,yarn等安装electron超时等问题

我在安装electron的时候&#xff0c;出现了超时等等各种问题&#xff1a; &#xff08;RequestError: connect ETIMEDOUT 20.205.243.166:443&#xff09; npm yarn&#xff1a;Request Error: connect ETIMEDOUT 20.205.243.166:443 RequestError: socket hang up npm ER…