【数据结构与算法】(14):堆排序和选择排序(超详细配图~)

🤡博客主页:Code_文晓

🥰本文专栏:数据结构与算法

😻欢迎关注:感谢大家的点赞评论+关注,祝您学有所成!


✨✨💜💛想要学习更多数据结构与算法点击专栏链接查看💛💜✨✨ 


1. 选择排序的概念和思想 

        前面我们一起学习了插入类排序《插入排序和希尔排序看这一篇文章就够了!》和交换类排序《交换排序之冒泡排序和快速排序》,这次,我们学习一 个新的排序种类——选择类排序。

       选择类排序 是一种基于比较的排序算法,他的基本思想就是每一趟在待排序的元素中选取关键字最小(或最大)的元素加入到有序子序列中。而简单选择排序则是选择类排序中的一种。  

        基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。       


2. 简单选择排序 

  1. 在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素

  2. 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换

  3. 在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

下面是简单选择排序的动态图效果: 

有了动态图,下面一步步来演示简单选择排序的过程,如图1所示:

图1左上侧是待排序的10个元素,我们以从小到大排序来说明。

  • 第一趟排序:从待排序的元素中找出值最小的元素和下标为0的元素做交换,这样下标为0的位置所保存的就是最小值的元素,此时无需再理会下标为0的位置。

  • 第二趟排序:抛开下标为0位置的元素,从其余元素中找出值最小的元素和下标为1的元素做交换,这样下标为1位置所保存的就是剩余待排序元素中最小值的元素,此时无需再理会下标为1的位置。

  • 第三趟排序:抛开下标为0、1位置的元素,从其余元素中找出值最小的元素和下标为2的元素做交换,这样下标为2位置所保存的就是剩余待排序元素中最小值的元素,此时无需再理会下标为2的位置。

  • 如此重复,第九趟排序:抛开下标为0、1、2、3、4、5、6、7位置的元素,从其余元素中找出值最小的元素和下标为8的元素做交换,这样下标为8位置所保存的就是剩余待排序元素中最小值的元素。此时,下标为9的元素自然就是值最大的元素。

到这里,整个简单选择排序算法就完成了。下面是的代码实现:

template<typename T>
void Swap(T* p1, T* p2)
{T tmp = *p1;*p1 = *p2;*p2 = tmp;
}//简单选择排序(从小到大)
template<typename T>
void SelectSort(T myarray[], int length)
{for (int i = 0; i < length - 1; ++i) //一共要进行length-1趟{int minidx = i;  //保存最小元素位置//在myarray[i]到myarray[length-1]中选择最小值for (int j = i + 1; j < length; ++j){if (myarray[j] < myarray[minidx])minidx = j; //保存最小元素值位置} //end for jif (minidx != i){Swap(&myarray[i], &myarray[minidx])}} //end for ireturn;
}

简单选择排序的优化 :

//简单选择排序优化(从小到大)
template<typename T>
void SelectSort(T myarray[], int length)
{int begin = 0, end = length- 1;while (begin < end){int maxidx = begin, minidx = begin;for (int i = begin; i <= end; i++){if (myarray[i] > myarray[maxidx]){maxidx = i;}if (myarray[i] < myarray[minidx]){minidx = i;}}Swap(&myarray[begin], &myarray[minidx]);if (begin == maxidx){maxidx = minidx;}Swap(&myarray[end], &myarray[maxidx]);++begin;--end;}
}

该算法的时间复杂度如何呢?我们用n代表元素数量,无论原有数据是否有序,都需要进行n-1趟处理,总共需要对比的关键字次数是(n-1)+(n-2)+…+1=\frac{n(n-1)}{2}次(等差数列求和公式)。而需要元素交换的次数最少为0次,最多也不会超过3(n-1)次。所以简单选择排序算法的时间复杂度为O(n^{2})空间复杂度为O(1)。

此外,该算法是不稳定的,只需要用一组数据2、2、1测试一下即可知道。如图2所示,前两个元素在排好序之后位置已经调换了。


3. 堆排序 

        简单选择排序说完之后,我们来说一下堆排序。这两类排序都属于选择类排序,不过两者的实现方式不同。简单选择排序是每趟从待排序的元素中找出值最小的元素放到已排序序列的末尾,直到所有元素排序完成。而堆排序是通过将待排序元素构成一个堆的方式来实现元素的排序,可以说,堆排序的效率往往更高。

        其实在本专栏中的这篇文章详细讲解了堆的概念性质和操作,其中在堆的应用中堆排序已经讲解的很详细了,大家可以看看这个文章《”堆“和二叉树顺序存储的”缘“》。既然我们学习到了排序专题,这里换一些配图和数据重新讲一下,大家也可以将两个内容对比一下学习,加强记忆。

堆的基本概念 

堆是有序的完全二叉树,这里所说的有序指的是父节点一定大于等于子节点值或者父节点一定小于等于子节点值。

根据概念可知,堆又分为大根堆和小根堆: 

  • 大根堆(大顶堆/最大堆):每个结点的值都大于或等于其左右孩子结点的值

  • 小根堆(小顶堆/最小堆):每个结点的值都小于或等于其左右孩子结点的值

我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中: 

该数组从逻辑上讲就是一个堆结构,这篇文章《树与二叉树的概念、性质及详细证明》学习过的二叉树的性质六我们知道:

性质六:对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则编号为i的节点有: 

若 i>0,i 位置节点的双亲序号:(i-1)/2;(若 i=0,i则是根节点的编号,无双亲节点
若 2i+1<n,左孩子序号:2i+1;(2i+1>=n否则无左孩子)
若 2i+2<n,右孩子序号:2i+2;(2i+2>=n否则无右孩子)

所以将堆的逻辑结构映射到数组中,我们用上述性质六的公式重新来描述一下堆的定义就是: 

  • 大根堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 
  • 小根堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2 

        如果实现从小到大的排序算法,则使用大顶堆比较方便,实现出的算法占用的辅助空间更少。如果实现从大到小排序,则使用小顶堆比较方便。  

        堆排序就是利用堆(大顶堆或者小顶堆)进行排序的方法。这里我将采用大顶堆来实现。

        堆排序的基本思想是:把待排序的n个元素的序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将该节点数据删除,实际是将该节点数据与堆待排序序列末尾元素进行交换, 此时末尾就为最大值。然后将剩余n-1个元素的序列重新构造成一个大顶堆,这样会得到n个元素的次大值。如此反复执行,便能得到一个有序序列了。

所以,堆排序需要解决两个主要问题。

  1. 由一个无序序列建成一个堆。

  2. 在输出堆顶元素(删除元素)后,对其余元素进行调整从而生成一个新堆。

1、构造初始堆 

将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。 

2、此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点为arr.length/2-1=5/2-1=1,也就是下标为1的节点6),从左至右,从下至上进行调整。 

3、找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换 

4、这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。 

此时,我们就将一个无需序列构造成了一个大顶堆。 

5、将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交 换,得到第二大元素。如此反复进行交换、重建、交换 。

将堆顶元素9和末尾元素4进行交换 

重新调整结构,使其继续满足堆定义

 再将堆顶元素8与末尾元素5进行交换,得到第二大元素8 

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序,就可以得到从小到大的排好序的序列了。

代码实现:

void Swap(int* p1, int* p2) // 两节点元素交换位置
void AdjustDown(int* myarray, int length, int parent) // 堆节点向下调整(下沉)void HeapSort(int* myarray, int length)
{// 升序 -- 建大堆// 降序 -- 建小堆// 1.从第一个非叶子节点(分支节点)开始,往前进行向下调整建堆for (int i = (length / 2) - 1; i >= 0; i--) //时间复杂度:O(length),推荐{AdjustDown(myarray, length, i);}//2.对堆节点进行调整排序//方法:第0个节点与最后一个节点交换、再从第0个节点开始往后进行向下调整int end = length - 1;while (end > 0){Swap(&myarray[0], &myarray[end]);AdjustDown(myarray, end, 0);end--;}
}// 两节点元素交换位置
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}// 堆的向下调整(下沉)
void AdjustDown(int* myarray, int length, int parent)
{int child = 2 * parent + 1; //先设左孩子节点较大while (child < length) //向下调整的终止条件为:双亲节点已经下沉到叶子节点位置,其child已大于数组的size越界{//选出左右孩子节点中较大的if (child + 1 < length && myarray[child + 1] > myarray[child]) //若右孩子节点存在且大于左孩子节点{child++; //设右孩子节点较大} //到这child为左右孩子中较大的节点if (myarray[child] > myarray[parent]){Swap(&myarray[parent], &myarray[child]);parent = child; //交换数据之后,可能就会影响到下一层了。所以还要继续向下调整child = 2 * parent + 1;}else{break;}}
}

        到这,堆排序的实现就完成了,有什么疑问都可以在评论区或者私信我。如果你想学习更多有关堆的操作,例如堆的插入和删除,就看看这一篇文章吧《”堆“和二叉树顺序存储的”缘“》!

        创作不易,如果您觉得本篇文章对您有一点帮助的话,希望您能点个赞支持我一下吧~⭐ 

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

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

相关文章

RuoYi-Vue开源项目2-前端登录验证码生成过程分析

前端登录验证码实现过程 生成过程分析 生成过程分析 验证码的生成过程简单概括为&#xff1a;前端登录页面加载时&#xff0c;向后端发送一个请求&#xff0c;返回验证码图片给前端页面展示 前端页面加载触发代码&#xff1a; import { getCodeImg } from "/api/login&q…

HarmonyOS(鸿蒙)ArkUI组件

方舟开发框架&#xff08;简称ArkUI&#xff09;为HarmonyOS应用的UI开发提供了完整的基础设施&#xff0c;包括简洁的UI语法、丰富的UI功能&#xff08;组件、布局、动画以及交互事件&#xff09;&#xff0c;以及实时界面预览工具等&#xff0c;可以支持开发者进行可视化界面…

jar读取目录配置、打包jar后无法获取目录下的配置

jar读取目录配置、打包jar后无法获取目录下的配置 jar读取目录配置、打包jar后无法获取目录下的配置。java打成jar包后获取不到配置文件路径。解决项目打成jar包上线无法读取配置文件。打包jar后无法读取resource下的配置文件 场景 需要读取 src/main/resources/mapper下的所…

Windows系统部署GoLand结合内网穿透实现SSH远程Linux服务器开发调试

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-HIOuHATnug3qMHzx {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

es 聚合操作(二)

书接上文&#xff0c;示例数据在上一篇&#xff0c;这里就不展示了 一、Pipeline Aggregation 支持对聚合分析的结果&#xff0c;再次进行聚合分析。 Pipeline 的分析结果会输出到原结果中&#xff0c;根据位置的不同&#xff0c;分为两类&#xff1a; Sibling - 结果和现有…

③【Docker】Docker部署Nginx

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ ③【Docker】Docker部署Nginx docker拉取nginx…

聚焦两会:数字化再加速,VR全景助力制造业转型

近年来&#xff0c;随着信息技术、人工智能、VR虚拟现实等新兴技术的不断涌现&#xff0c;数字化正日益成为推动当今经济发展的新驱动力。在不久前的两会上&#xff0c;数字化经济和创新技术再度成为热门话题&#xff1a; 国务院总理李强作政府工作报告&#xff1a; 要深入推…

【Session】Tomcat Session 集群

设备 nginx&#xff1a;192.168.67.11 tomcat1&#xff1a;192.168.67.12 tomcat2&#xff1a;192.168.67.13安装nginx &#xff08;192.168.67.11&#xff09; #关闭防火墙和安全机制 [roottest1 ~]# systemctl stop firewalld [roottest1 ~]# setenforce 0#安装epel源 [ro…

Postman接口做关联测试的方法步骤

应用场景 假设下一个接口登录需要上一个接口的返回值&#xff0c;例如请求需要先登录获取到token&#xff0c;下一个请求要携带对应的token才能进行请求 方法&#xff1a;通过设置全局变量/环境变量 方法一&#xff1a;设置全局变量 1.先请求登录接口&#xff0c;请求成功之后…

HarmonyOS ArkTS 开发基础/语言

目录 一、ArkUI (方舟开发框架) 概述 1.1 基本概念 1.2 两种开发范式 1.3 不同应用类型支持的开发范式 二、ArkTS 声明式开发范式 2.1 开发能力 2.2 整体架构 三、ArkTS 基础类型 3.1 Any 类型 3.2 数字类型 3.3 字符串类型 3.4 布尔类型 3.5 联合类型 3.6 数组类…

【leetcode】67.二进制求和

前言&#xff1a;剑指offer刷题系列 问题&#xff1a; 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 示例&#xff1a; 输入&#xff1a;a "1010", b "1011" 输出&#xff1a;"10101"思路1&#xff1a; …

Kotlin,简直是 Java 的 Pro Max!(笔记4 协程篇)

目录 Kotlin 协程 为什么引入协程&#xff1f; 协程和线程有什么区别&#xff1f; 协程的使用 依赖 GlobalScope.launch runBlocking 创建多个协程 delay suspend 关键字 coroutineScope job async withContext 函数 Kotlin 协程 为什么引入协程&#xff1f; 1. …

GESP图形化编程二级认证真题 2024年3月

GESP 图形化二级试卷 &#xff08;满分&#xff1a;100 分 考试时间&#xff1a;120 分钟&#xff09; 一、单选题&#xff08;共 10 题&#xff0c;每题 3 分&#xff0c;共 30 分&#xff09; 1、小杨的父母最近刚刚给他买了一块华为手表&#xff0c;他说手表上跑的是鸿…

蓝桥杯2023省赛:矩阵总面积|模拟、数学(几何)

题目链接&#xff1a; 0矩形总面积 - 蓝桥云课 (lanqiao.cn) 说明&#xff1a; 参考文章&#xff1a;矩形总面积计算器&#xff1a;计算两个矩形的总面积&#xff0c;包括重叠区域_矩形r1的左下角坐标为x1, yl 、宽度为w1、高度为h1, 矩形r2的左下角坐标为x2,y2、宽-CSDN博客…

React 系列 之 React Hooks(一) JSX本质、理解Hooks

借鉴自极客时间《React Hooks 核心原理与实战》 JSX语法的本质 可以认为JSX是一种语法糖&#xff0c;允许将html和js代码进行结合。 JSX文件会通过babel编译成js文件 下面有一段JSX代码&#xff0c;实现了一个Counter组件 import React from "react";export defau…

【OJ比赛日历】快周末了,不来一场比赛吗? #03.23-03.29 #16场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2024-03-23&#xff08;周六&#xff09; #7场比赛2024-03-24…

Unity InputField实现框自适应内容简便方法

要实现InputField框自适应输入内容&#xff0c;除了通过代码进行处理&#xff0c;还可以是使用以下简便的方法。 1、创建InputField组件&#xff1a;右键->UI->Input Field -TextMeshPro。 2、把Input Field Settings中的Line Type设置为Multi Line Newline模式&#x…

8.2K star!史上最强Web应用防火墙

&#x1f6a9; 0x01 介绍 长亭雷池SafeLine是长亭科技耗时近 10 年倾情打造的WAF(Web Application Firewall)&#xff0c;一款敢打出口号 “不让黑客越雷池一步” 的 WAF&#xff0c;我愿称之为史上最强的一款Web应用防火墙&#xff0c;足够简单、足够好用、足够强的免费且开源…

php laravel 二维码

public function qr($url,$name2,$inpath){require_once(dirname(__FILE__) . /../../../Library/phpqrcode/phpqrcode.php);$errorCorrectionLevel L;//容错级别$matrixPointSize 10;//生成图片大小$QRcode new \QRcode() ;$QRcode->png($url, $inpath.$name2, $errorCor…

【Spring 篇】走进Java NIO的奇妙世界:解锁高效IO操作的魔法

欢迎来到Java NIO的神奇之旅&#xff01;在这个充满活力的世界里&#xff0c;我们将一起揭示Java NIO&#xff08;New I/O&#xff09;的奥秘&#xff0c;探索其在高效IO操作中的神奇魔法。无需担心&#xff0c;即使你是Java的小白&#xff0c;也能轻松领略这个强大而灵活的IO框…