八大算法排序@堆排序(C语言版本)

目录

  • 堆排序
    • 大堆排序
      • 概念
      • 算法思想
        • 建堆
        • 建堆核心算法
        • 建堆的代码
      • 排序代码实现
    • 小堆排序
      • 代码实现
      • 时间复杂度
      • 空间复杂度

堆排序

  堆排序借用的是堆的特性来实现排序功能的。大堆需要满足父节点大于子节点,因此堆顶是整个数组中的最大元素。小堆则相反,要求父节点小于子节点,因此父节点是整个数组中最小元素。
借助这一特性,对于大堆,我们可以将堆顶的元素,和堆的最后一个元素置换,这样便将最大的数排到了最后面。同时将堆顶的有效个数减1。但是置换上来堆顶的元素,也就破坏了堆的结构,但是堆顶元素的左右子节点依旧是堆的结构,因此可以对堆顶进行调整,然其继续满足堆的特性。这样便找出第二大的元素,继续重复上述动作,便能将大的数组都往后挪动。待到只剩下一个有效数据时,便拍好了数组。大家认为这是一个升序的数组呢?还是一个降序的数组呢?
  答案显而易见,使用大堆进行排序,结果是升序的效果。同样的思路,使用小堆进行排序,结果是降序的结果。

因此,得出一个结论:
想要对数组进行升序的排序,使用大堆;
想要对数组进行降序的排序,使用小堆;



大堆排序

概念

  大堆概念:每个节点(父节点)的值都大于或等于其子节点的值。
大堆排序借用的便是大堆的特性,将堆顶的元素和堆的最后一个元素进行置换,置换上来堆顶的数据再进行堆结构的调整。然后重复以上动作,实现排序功能。



算法思想

要想实现大堆排序,首先需要对数组建立起大堆的结构。下面我们使用数组 arr[] = { 6 , 4 , 3 , 9 , 2 , 1 ,5 ,7 ,8 };来模拟演示大堆的构建,以及排序的大体过程。如下是数组的初始状态:

数组初始状态

首先,让我们来看看堆的结构是如何产生的。

建堆

  首先,要明确一点的就是:堆是基于完全二叉树而演变出来的一种数据结构。分为最大堆和最小堆两种类型,但它们都是完全二叉树。完全二叉树结构特点如下:

完全二叉树
注意:完全二叉树每个节点必须是从左到右依次遍布的,中间不能跳跃,如第四层的第三个节点,必须是第三层第二个节点的左节点,否则将不满足完全二叉树的特点。

  了解以上知识后,还得须知建堆的核心算法是 “向下调整算法” 。通过算法的一步步调整,一步步建立起堆这一结构。但是!该算法有个前提,就是调整的节点的左右子节点必须是堆的结构。
  有同学可能有疑惑,这不是扯淡吗?我就是要建立的堆结构,又要我满足调整的节点的左右子节点也是堆结构,闹着玩儿呢?
  并不是的,正所谓没有条件,便创造出条件。我们可以从下往上进行建堆啊!比如回到原数组 arr[] = { 6 , 4 , 3 , 9 , 2 , 1 ,5 ,7 ,8 };我们先对数组模拟出堆的结构出来:

原数组

肉眼可见的,原数组模拟出来的完全二叉树,并不符合堆的结构特点。那么如何做到前面所说的,创造出前提条件,进行建堆呢?从堆顶向下进行调整是不可能的,因为堆顶的元素6不满足核心算法的前提条件。但是!我们可以对元素9进行调堆啊,元素9不就正好满足核心算法的前提条件嘛。
首先元素9有左右两个子节点7和8。元素7和元素8左右子节点都为空节点,那么元素7和元素8可以认为是堆的结构,因此元素9左右子节点都是堆结构这一前提条件便满足了。可以使用建堆的核心算法,从元素9开始依次往上建堆。
  元素9建成堆结构后,下标减1。开始对元素3进行建堆,同样的元素3也满足核心算法的前提条件,同样可以使用核心算法进行堆结构的建立。接着就是元素4,元素4此时也满足了左右子节点都是堆结构的条件,同样进行堆的调节建立,最终是元素6也就是堆顶的调节,而这时堆顶也满足了左右子节点都是堆结构的前提条件,同样利用 “向下调整算法” 进行堆结构的建立。至此完成整个数组的堆结构构建。

注:
是如何找到要从哪一个节点进行堆的构建的?利用子节点找父节点的方法!如数组有n个元素,最后一个元素的下标则为n-1,根据完全二叉树的特点,最后一个元素的父节点的下标为:(n-1-1)/ 2。




建堆核心算法
// 两数交换
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}// 向下调整算法
/*
传入参数:
ret:需要建立的数组
root:需要开始调节的节点
n:数组的个数
*/
void AdjustDown(int* ret,int n,int root)
{int parent = root;// 父节点,也就是要调节的节点int child = parent*2+1;// 子节点/孩子,默认是左节点while(child<n){// 找出左右孩子,最大的孩子,同时要考虑只有左孩子的情况if(child+1<n && ret[child+1] > child[child]){child++;// 如果右孩子比左孩子大,则存储右孩子的下标}// 如果 子节点大于父节点,则子节点向上进行调整if(ret[parent] < ret[child]){swap(ret[parent],ret[child]);parent = child;child=parent*2+1;}else  // 父节点大于子节点则退出循环(注意父节点左右子节点都是堆的结构){break;}}
}




建堆的代码
// 升序 - 建大堆
void CreateHeap(int* a, int n)
{// 建大堆,从最后一个节点的父节点开始建立for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDowm(a, n, i);}
}

建完堆后的数组如下所示:

建堆完成

至此,我们实现了对数组建堆这一个数据结构的条件。接下来便能对其进行排序了。



排序代码实现

void SortHeap(int* a,int n)
{// 排序for (int i = 1; i < n; i++){Swap(&a[0], &a[n - i]);	// 将堆顶元素与堆的最后一个元素交换AdjustDowm(a, n  - i, 0); // 交换上堆顶的元素进行堆的调节,使其依旧保持大堆的结构特性}}

至此,便借用大堆的特性,实现了升序的排序效果。








小堆排序

概念和算法思想和大堆差不多,便不过多冗余介绍了,下面直接给出小堆排序的实现的降序排序代码。




代码实现

// 两数交换
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}// 向下调整算法 - 前提:左右子树都是堆
void AdjustDowm(int* a, int n, int root)
{int parent = root;int child = parent * 2 + 1;//默认左孩子while (child < n){// 找出左右子节点中,最小的子节点if (child + 1 < n && a[child + 1] < a[child]){child++;}// 如果父节点比子节点大,则将子节点向上调整if (a[parent] > a[child]){Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else{break;	// 退出循环}}
}// 降序 - 建小堆
void HeapSort(int* a, int n)
{// 建大堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDowm(a, n, i);}//PrintArr(a,n);//查看建堆后的数组// 排序for (int i = 1; i < n; i++){Swap(&a[0], &a[n - i]);AdjustDowm(a, n  - i, 0);}}

与大堆的差别仅改变了核心算法中的两处地方,如下:
在这里插入图片描述

下面介绍一下堆排序的时间复杂度和空间复杂度。




时间复杂度

O(N*logN)
大体计算如下:
建堆时间复杂度为O(N);
堆排序的时间复杂度为O(N*logN),因为将堆顶元素置换以后,还需要进行调堆,调堆的时间复杂度为O(logN)。每排一个元素需要进行一次调堆,所以排完整个数组的时间复杂度为O(N*logN)。
所以总的时间复杂度为:O(N+N*logN)。两者记录较大的,所以时间复杂度为O(N*logN)。




空间复杂度

O(1);

堆排序过程中,都是在原数组上进行的,没有申请空间,所以空间复杂度为O(1)。

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

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

相关文章

opencv和gdal的读写图片波段顺序问题

最近处理遥感影像总是不时听到 图片的波段错了&#xff0c;一开始不明就里&#xff0c;都是图片怎么就判断错了。 1、图像RGB波段顺序判断 后面和大家交流&#xff0c;基本上知道了一个判断标准。 一般来说&#xff0c;进入人眼的自然画面在计算机视觉中一般是rgb波段顺序表示…

基于Java+SpringBoot+vue实现图书借阅管理系统

基于JavaSpringBootvue实现图书借阅和销售商城一体化系统 &#x1f345; 作者主页 程序设计 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; 文章目录 基于JavaSpringBootvue实现图书借阅和销售商城一体化…

挑战Python100题(9)

100+ Python challenging programming exercises 9 Question 81 Please write a program to randomly print a integer number between 7 and 15 inclusive. Hints: Use random.randrange() to a random integer in a given range. 请编写一个程序,随机打印一个介于7和15之间…

c++ / day04

1. 整理思维导图 2. 全局变量&#xff0c;int monster 10000;定义英雄类hero&#xff0c;受保护的属性string name&#xff0c;int hp,int attck&#xff1b;公有的无参构造&#xff0c;有参构造&#xff0c;虚成员函数 void Atk(){blood-0;}&#xff0c;法师类继承自英雄类&a…

MySQL常见面试题总结

1.MySQL基础 1.1什么是关系型数据库&#xff1f; 顾名思义&#xff0c;关系型数据库&#xff08;RDB&#xff0c;Relational Database&#xff09;就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系&#xff08;一对一、一对多、多对多…

Leetcode11-快乐数(202)

1、题目 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为 1…

网络通信理论-入门1

网口框架 100M 2. 物理层解读 2.1 同步的方法&#xff1a;编码 为了让接收方在没有外部时钟参考的情况也能确定每一位的起始、结束和中间位置&#xff0c;在传输信号时不直接采用二进制编码。在 10BASE-T 的传输方式中采用曼彻斯特编码&#xff0c;在 100BASE-T 中则采用 4B/…

cJSON代码解读

1、背景 cJSON用了很久&#xff0c;但是对它一直不太了解。这次向添加对long long类型的支持&#xff0c;一直出问题。因为有以前添加两位小数float的经历&#xff0c;我觉得会很轻松&#xff0c;没想到翻车了。于是有了这边文档&#xff0c;阅读了部分博主对cJSON的解析&…

02--数据定义语言DDL

1、数据定义语言DDL 1.1 操作数据库-DDL 创建数据库 create database 数据库名称; 创建数据库&#xff0c;并指定字符集 create database 数据库名称 character set 字符集名; 查询所有数据库的名称 show databases; 查询某个数据库的字符集:查询某个数据库的创建语句及字…

SpringBoot: 通过MyBatis访问ClickHouse

一、ClickHouse中建表&#xff0c;添加数据 二、SpringBoot项目添加mybatis、clickhouse、druid相关依赖 <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.6</version></dependency>…

stm32 HAL库 4096线ABZ编码器

[TOC]目录 ABZ编码器 4096线 买的是这个 AB相代表计数方向&#xff0c;Z代表过零点 cubemx配置 定时器Encoder 也可以选上DMA 中断 Z相GPIO中断 找一个空闲管脚 打开对应中断 代码 不用DMA int main(void) {short Enc_cnt 0;HAL_TIM_Encoder_Start_IT(&ht…

CSS 向上扩展动画

上干货 <template><!-- mouseenter"startAnimation" 表示在鼠标进入元素时触发 startAnimation 方法。mouseleave"stopAnimation" 表示在鼠标离开元素时触发 stopAnimation 方法。 --><!-- 容器元素 --><div class"container&q…

逻辑回归(LR)----机器学习

基本原理 逻辑回归&#xff08;Logistic Regression&#xff0c;LR&#xff09;也称为"对数几率回归"&#xff0c;又称为"逻辑斯谛"回归。 logistic回归又称logistic 回归分析 &#xff0c;是一种广义的线性回归分析模型&#xff0c;常用于数据挖掘&#…

五、Spring AOP面向切面编程(基于注解方式实现和细节)

本章概要 Spring AOP底层技术组成初步实现获取通知细节信息切点表达式语法重用&#xff08;提取&#xff09;切点表达式环绕通知切面优先级设置CGLib动态代理生效注解实现小结 5.5.1 Spring AOP 底层技术组成 动态代理&#xff08;InvocationHandler&#xff09;&#xff1a;…

改变传媒格局的新趋势

在如今信息高速发展的时代&#xff0c;人们早已进入了一个以手机为中心的智能化时代。随着科技的迅猛发展&#xff0c;手机无人直播成为了一种新兴的传媒形态&#xff0c;正逐渐改变着传媒格局。本文将从手机无人直播的定义、发展背景和影响等方面进行探讨。 首先&#xff0c;…

关于我花费六千多组了台window+Linux主机

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 写在前面 我在2023年12月组了一台“洋垃圾”的主机&#xff0c;一边当做台式机使用&#xff0c;一边当做服务器使用。这个方案算是相对比较划算的方案。我开始是打算直接单做服务器使用的&#xff0c;以及内存…

ROS学习笔记(7)进一步深入了解ROS第一步

0.前提 最近在学习宾夕法尼亚大学工程学院的ROS公开课&#xff0c;在尽力的去融入全英语的环境&#xff08;哪怕我的英语水准并不是很高&#xff09;。既然是在学习&#xff0c;笔记也就是必须的了&#xff0c;当然这些笔记都是课程当中提出的问题&#xff0c;我去寻找后得出的…

【STM32】I2C通信

基本的任务是&#xff1a;通过通信线&#xff0c;实现单片机读写外挂模块寄存器的功能。其中至少要实现在指定位置写寄存器和在指定的位置读寄存器这两个功能。 异步时序的优点&#xff1a;省一根时钟线&#xff0c;节约资源&#xff1b;缺点&#xff1a;对事件要求严格&#…

20231228在Firefly的AIO-3399J开发板的Android11的挖掘机的DTS配置单前置摄像头ov13850

20231228在Firefly的AIO-3399J开发板的Android11的挖掘机的DTS配置单前置摄像头ov13850 2023/12/28 10:42 【碰到一个很神奇的问题】&#xff1a; 昨天晚上前置摄像头怎么也点不亮&#xff01;改了巨多的地方&#xff01;晚上睡觉之前把开发板彻底断电了&#xff01;今天开电脑…

深度生成模型之GAN基础 ->(个人学习记录笔记)

文章目录 深度生成模型之GAN基础生成对抗网络1. 生成对抗网络如何生成数据2. 生成对抗原理3. GAN的核心优化目标4. D的优化5. GAN的理想状态6. GAN的训练7. 梯度不稳定与模式崩塌(collapse mode)问题8. 梯度消失问题 深度生成模型之GAN基础 生成对抗网络 1. 生成对抗网络如何…