【数据结构】8.4 选择排序

news2024/12/24 11:41:08

文章目录

  • 1. 简单选择排序
    • 简单选择排序算法
    • 简单排序算法分析
  • 2. 堆排序
    • 堆的定义
    • 堆的调整
    • 堆的建立
    • 堆排序算法
    • 堆排序算法分析

1. 简单选择排序

基本思想

  • 在待排序的数据中选出最大(小)的元素放在其最终的位置。

基本操作

  1. 首先通过 n - 1 次关键字比较,从 n 个记录中找出关键字最小的记录,将它与第一个记录交换。
  2. 在通过 n - 2 次比较,从剩余的 n - 1个记录汇总找出关键字次小的记录,将它与第二个记录交换。
  3. 重复上述操作,总共进行 n - 1 趟排序后,排序结束。

举个栗子

  1. 把最小的数据元素挑出来,放到第一个位置。

在这里插入图片描述

  1. 找出第二小的数据元素,放到第二个位置上。

在这里插入图片描述

  1. 接下来找出第三小的元素,放到第三个位置上。

在这里插入图片描述

  1. 之后就依次类推,跑完 6-1=5 趟之后,得到排序完成为止。

在这里插入图片描述

结论

  • 虽然每一趟都要找一个最小值,但是找最小值的范围是不同的。
    • 第一趟从第二个元素开始,直到最后一个元素找最小值和第一个位置进行交换,第二趟从第二个开始到最后找出最小值和第二个交换……
    • 第 i 趟从第 i 个元素开始,到最后一个元素找最出小值和第 i 个进行交换

找最小值

  • 假设第 1 个元素 A 为当前最小值,然后依次将 A 与之后面的元素进行比较。
  • 如果后面出现了比 A 小的值 B,则 B 的位置记录下来,将最小值更新为 B。
  • 然后用新的最小值 B 和后面的元素进行比较;
    • 出现比 B 小的值 C 就将最小值更新成 C 继续执行上述步骤,
    • 如果直到最后都没直到比 B 小的,则 B 为最小值,将其放到第 i 个位置。

简单选择排序算法

//对顺心表L做简单做简单选择排序
void SelectSort(SqList &K)
{
		for(i = 1;i < L.length;i++)//在L.e[i...L.length] 中选择关键字最小的记录
		{
				k = i;//将当前最小值的位置用K记录下来
				for(j = i + 1;j <= L.length;j++)
				{
						if(L.r[j].key < L.r[k].key) //某个元素比下标为k的还小,则它就是当前的最小值
						{
								k = j;//k指向此趟排序中关键字最小的记录
						}						
				}
				if(k != i)	//如果第一个不是最小值了,则交换 r[i] 与 r[k]
				{
						t = L.r[i];
						L.r[i] = L.r[k];
						L.r[k] = t;
				}
		}
}

简单排序算法分析

时间复杂度

  • 记录移动次数:
    • 最好情况:0。
    • 最坏情况:3(n-1)。
  • 比较次数:无论待排序列处于什么状态,选择排序所需进行的比较次数都相同。

在这里插入图片描述

  • 时间复杂度:O(n2)

算法稳定性

  • 简单选择排序是不稳定排序。

在这里插入图片描述

空间复杂度

  • 在进行交换的时候,需要一个额外的辅助空间,所以时间复杂度为:O(1)

2. 堆排序

堆的定义

  • 小根堆:第 i 个位置上的元素小于第 2i 及第 2i+1 位置上的元素;
    • 如:第 2 个位置上的元素必须小于第 4 及第 5 个元素。
  • 大根堆:与小根堆正好相反。

在这里插入图片描述

  • 从堆的定义可以看出,堆实际上是满足如下性质的完全二叉树
    • 二叉树中任一非叶子结点均小于(大于)等于它的左右孩子结点
    • 根结点的值小于等于左右孩子的称作小根堆,大根堆反之。

举个例子

【例1】判断以下两组数据是否是堆:

  • {98,77,35,53,55,14,35,45}
  • {14,48,35,62,55,98,35,77}
    • 按照自上而下、自左而右的方式,依次所有的数据数据,看看是否符合大、小根堆的定义。

在这里插入图片描述

【例2】判断是否是堆?

在这里插入图片描述

堆排序

  • 若在输出堆顶的最小值(最大值)后,使得剩余 n-1 个元素的序列重新又建立成一个堆,则得到 n 个元素的次小值(次大值)……如此反复,便能得到一个有序序列,这个过程称之为堆排序
    • 在大根堆中,根结点就是树的最大值,小根堆则是最小值。
    • 将堆顶拿走之后,将剩下的元素重新调整成一个堆,那么新堆的根节点就是当前的最大(最小)值了。

实现堆排序需要解决两个问题

  1. 堆建立:如何由一个无序序列建成一个堆?
  2. 堆调整:如何在输出堆顶元素后,调整剩余的元素为一个新堆?

堆的调整

如何在输出堆顶元素之后,调整剩余元素为一个新的堆?

小根堆

  1. 输出堆顶元素之后,以堆中最后一个元素 A 代替它成为根节点
  2. 然后将根节点 A 的值与左、右子树的根节点值进行比较,并与其中小者进行交换
  3. 重复上述操作,直到将 A 调整回叶子结点,就能得到新的堆,将这个从堆顶至叶子的调整过程称为筛选

大根堆的调整方式与小根堆相同,只不过变成了让根结点与比它大的左右孩子进行交换而已。

举个例子

当前有一棵小根堆构成的二叉树。

在这里插入图片描述

  1. 输出根节点并用最后一个元素代替它,接下来就一只进行交换调整,直到将这个结点调整回叶子结点去。

在这里插入图片描述

  1. 将它的左右孩子的值与它进行比较,并和其中的较小值进行交换。

在这里插入图片描述

  1. 将 97 拿下来之后它并没有成为叶子,所以继续将其与左右孩子比较然后交换。
    • 当此结点成为叶子结点之后,小根堆调整完毕。
    • 之后再输出堆顶元素然后调整的过程相同。

在这里插入图片描述

筛选过程算法

/*已知R[s...m]中记录的关键字除了R[s]之外均满足堆的定义,
将R[s...m]调整为以R[s]为根的大根堆*/
void HeapAdjust(elem R[],int s,int m)
{
		rc = R[s];
		for(j = 2*s;j <= m;j *= 2)//沿着key较大的孩子结点向下筛选
		{
				if(j < m&& R[j] < R[j+1])
				{
						++j;//j为key较大的孩子结点的下标
				}
				if(rc >= R[j])
				{
						break;
				}
				R[s] = R[j];
				s = j;//rc应该插入在位置s上
		}
		R[s] = rc;//插入

}
  • s 是根节点下标,m是数组最大下标,j 被赋值为 s 的左孩子下标;
  • 第一个 if 对两个子节点进行比较,选最大的子节点,在第二个 if 中与根节点的值比较,如果根节点的值更大则直接退出,保持不变,否则将大的子节点的值赋值给根节点的位置;
  • 然后将子节点的下标赋值给 s,进行循环继续比较,循环结束后将之前存的根节点的值赋值在叶子节点上。

结论

  • 对一个无序序列反复删选就可以得到一个堆;
  • 即:从一个无序序列建堆的过程就是一个反复筛选的过程。

堆的建立

如何由一个无序序列建成一个堆?

显然

  • 只有一个结点的二叉树是堆;
  • 在完全二叉树中,所有序号大于 n /2 的结点都是叶子结点,因此以这些结点为根的子树均已是堆。
    • 所有的叶子结点都不需要调整,它已经是堆了。
    • 只需要调整所有的非叶子结点。
    • 根据完全二叉树的性质,最后一个叶子如果为 n ,那么它的双亲结点为 n / 2,也就数说,最后一个非叶子结点是 n / 2
  • 只需要利用筛选法,从最后一个非叶子结点 n / 2 开始,依次将序号为 n / 2、(n / 2) - 1…、1 的结点作为根的子树都调整为根即可。

由于堆实质是一个线性表,那么我们就可以顺序存储一个堆

例:建造一个小根堆

有关键字为:49,38,65,97,76,13,27,49 的一组记录,将其按照关键字调整为一个小根堆。

  1. 先把这些元素按照序号自上而下、自左而右的顺序建立一棵初始完全二叉树。

在这里插入图片描述

  1. 从最后一个非叶子结点开始,开始往前依次调整:
    • 调整从第 n / 2 个元素开始,将以该节点为根的二叉树调整为小根堆。
    • 将 97 和 49 两个结点交换一下,对应的数组位置也要交换。

在这里插入图片描述

  1. 将以序号为 n/2 - 1的结点作为根的二叉树调整为堆。
    • 现在 3 号位置的结点调整完了,接下来该调整 2 号结点了。

在这里插入图片描述

  1. 现在该调整 2 号结点了,将以序号为 n/2 - 2 的结点为根的二叉树调整为堆;
    • 发现 38 的左右孩子都比它大,不需要要调整。

在这里插入图片描述

  1. 再将以序号为 n/2 - 3 的结点为根的二叉树调整为堆。
    • 将根节点 49 与 13 交换位置,此时还没有调整结束,还需要将根结点 49 调整到叶子结点才行。
    • 最后将 49 与 27 交换位置,至此,整个无序树就变成了一个小根堆了。

在这里插入图片描述

算法描述

将初始无序的R[1]到R[n]建成一个小根堆,可用一下语句实现:

for(i = n/2;i >= 1;i--)
{
		HeapAdjust(R,i,n);
}

堆排序算法

由以上分析知:

  • 若对一个无序序列建堆,然后输出根;重复该过程就可以由一个无序序列输出为有序序列。
  • 实际上,堆排序就是利用完全二叉树中父结点与孩子结点之间的内在关系来排序的(实际就是二叉树的性质5)。

堆排序算法

//对R[1]到R[n]进行堆排序
void HeapSort(elem R[])
{
		int i;
		for(i = n/2;i >= 1;i--)//从最后一个非叶子结点到第一个来建立初始堆
		{
				HeapAdjust(R,i,n);//建立初始堆
		}
		for(i = n;i > 1;i--)//进行n-1趟排序
		{
				Swap(R[1],R[i]);//将根与最后一个元素交换
				HeapAdjust(R,j,i-1);//对R[1]到R[i-1]重新建堆
		}
}

堆排序算法分析

时间复杂度

  • 初始堆化所需时间不超过 O(n)。
  • 排序阶段(不含初始堆化)
    • 一次重新堆化所需时间不超过 O(logn)。
    • n-1 次循环所需时间不超过 O(nlogn)。
      • Tw(n) = O(n) + O(nlogn) = O(nlogn)
  • 堆排序的时间主要耗费在建立初始堆和调整建新堆时进行的修复筛选上。
    • 堆排序在最坏情况下,其时间复杂度也为 O(nlog₂n),这是堆排序的最大有点。
    • 无论待排序中的记录时正序还是逆序排列,都不会使堆排序处于“最好”或“最坏”的状态。

空间复杂度

  • 仅需要一个大小供交换用的辅助空间,所以空间复杂度为 O(1)

堆排序算法特点

  1. 不稳定排序。
  2. 只能用于顺序结构,不能用于链式结构。
  3. 初始建堆所需的比较次数较多,因此记录数较少时不宜采用。
    • 堆排序在最坏情况下时间复杂度为 O(nlog₂n),相对于快速排序最坏情况下的 O(n2) 而言是一个优点,当记录较多时比较高效。

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

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

相关文章

计算机SCI期刊能一稿多投吗? - 易智编译EaseEditing

首先建议不要一稿多投&#xff0c;投稿前要对目标期刊了解清楚&#xff0c;是什么方向&#xff0c;什么水平的。 可以看看期刊近期发表的文章&#xff0c;是什么方向的&#xff0c;这样会更精准。 一稿多投就是广撒网嘛&#xff0c;还不如做好功课&#xff0c;找到对应期刊&a…

网络知识详解之:HTTPS通信原理剖析(对称、非对称加密、数字签名、数字证书)

网络知识详解之&#xff1a;HTTPS通信原理剖析&#xff08;对称、非对称加密、数字签名、数字证书&#xff09; 计算机网络相关知识体系详解 网络知识详解之&#xff1a;TCP连接原理详解网络知识详解之&#xff1a;HTTP协议基础网络知识详解之&#xff1a;HTTPS通信原理剖析&…

Es6 扩展运算符... ,以及rest和arguments

扩展运算符... … 扩展运算符能将 数组 转换为逗号分隔的 参数序列 应用场景&#xff1a; 多个数组的合并 var arr4 [1, 2, 3];var arr5 [4, 5, 6];var arr6 [...arr4, ...arr5];//合并数组 也可以为数组的一部分arr6的值应为[1,2,3,4,5,6] 数组的克隆&#xff0…

C语言--指针与数组

目录指针运算&#xff08;补&#xff09;指针指针指针的关系运算&#xff08;补&#xff09;指针与数组数组名二级指针指针数组指针运算&#xff08;补&#xff09; 指针指针 上一篇博客我们介绍了指针运算中的三种常见运算&#xff1a;指针整数&#xff0c;指针关系运算&…

关于android studio安装篇

前言&#xff1a;本文安装环境为windows系统&#xff0c;调试环境AVD&#xff08;电脑上运行的“虚拟手机”&#xff09;&#xff0c;安装android studio之前需安装jdk&#xff0c;配置好jdk的环境变量。解释&#xff1a;android运行调试环境有三种方式&#xff0c;真机、AVD&a…

基于uboot的truested安全启动模式进行TF-A源码移植

步骤 1. 对源码进行解压 tar -xvf tf-a-stm32mp-2.2.r2-r0.tar.gz2. 将补丁文件全打上 for p in ls -1 ../*.patch; do patch -p1 < $p; done3. 配置交叉编译工具链&#xff0c;在TF-A顶层目录下打开Makefile.sdk文件修改&#xff0c;搜索cross_compile&#xff0c;然后进…

5.组件钩子函数

目录 1 生命周期与钩子函数 2 创建时 3 更新时 3.1 执行顺序 3.2 触发条件 3.3 componentDidUpdate() 4 卸载时 1 生命周期与钩子函数 类组件从 被挂载到页面中运行&#xff0c;到组件不用时卸载 之间是组件的生命周期 只有类组件才有生命周期 生命周期的每个…

MATLAB算法实战应用案例精讲-【数模应用】概率生成模型(Generative Model)(补充篇)

前言 几个高频面试题目 非概率模型和概率模型的区别 非概率模型 非概率模型指的是直接学习输入空间到输出空间的映射h,学习的过程中基本不涉及概率密度的估计,概率密度的积分等操作,问题的关键在于最优化问题的求解。通常,为了学习假设 ,我们会先根据一些先验知识(pri…

道路千万条,安全第一条

我是腾讯安全的冯帆&#xff0c;主要在MMS安全服务团队负责交通行业保障工作。每年春节对交通行业来说都是一次考验&#xff0c;尤其是今年春运&#xff0c;也是三年来最具规模的一次“大考”。因此我们今年投入的人力&#xff0c;包括值守团队&#xff0c;也是这些年最多的一次…

Eclipse中的Classpath及【其与buildpath之间的关系】

Eclipse中的Classpath及【其与buildpath之间的关系】简介.classpath文件设置和查看方法通过eclipse设置通过Java代码设置通过eclipse菜单查看通过Java代码输出查看classpath及与buildpath之间的关系简介 Classpath .classpath文件 .classpath文件用于记录项目编译环境的所有…

第五届字节跳动青训营 前端进阶学习笔记(九)Node.js 与前端开发实战

文章目录前言Node.js的应用场景1.典型应用场景Node.js运行时结构特点Node.js实战1.搭建一个基本的http服务器2.返回json数据3.编写一个简单的静态服务器4.SSR5.调试6.部署总结前言 本堂课程重点内容&#xff1a; Node.js的应用场景Node.js的运行时结构Node.js实战 Node.js的应…

使用Markdown写出一份漂亮的简历

文章目录背景Markdown编辑器开始写你的简历个人优势项目经历            牧竹子个人技能公司经历项目经验XXXXXX项目 202N.N0.15-202N.N.N导出文档格式高级版修改样式背景 每当你要更新简历时是否因为各个平台不同输入框而陷入适配不同的模板&#xff0c;如此以来为…

全球开源学习平台Moodle

今天是正月初九&#xff0c;老苏开始上班了&#xff0c;起航新程&#xff0c;开工大吉&#xff01; 什么是 Moodle &#xff1f; Moodle 是一个开源的在线教育系统&#xff08;慕课&#xff09;。旨在为教育工作者、管理人员和学习者提供一个强大、安全且集成的系统&#xff0c…

DP背包问题

目录 一、前言 二、0/1背包 1、装箱问题&#xff08;lanqiaoOJ题号763&#xff09; 2、2022&#xff08;2022年国赛填空题&#xff0c;lanqiaoOJ题号2186&#xff09; 三、完全背包 1、小明的背包2&#xff08;lanqiaoOJ题号1175&#xff09; 四、分组背包 五、多重背包…

Kylin 4.0.1 分布式集群安装部署

目录1. 安装依赖2. 安装Kylin(kylin1上操作)2.1 下载解压2.2 修改conf/kylin.properties2.3 下载Mysql驱动包和创建Mysql数据库2.4 hive-site.xml复制到spark的conf目录2.5 配置环境变量(两台kylin服务器)2.6 kylin运行环境检查2.7 ConfigurationException问题(没有则忽略)2.8 …

神经网络--从0开始搭建全连接网络和CNN网络

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 今天来学习一下如何从0开始搭建全连接网络和CNN网络&#xff0c;并通过实验简单对比一下两种神经网络的不同之处&#xff0c;本文目录较长&#xff0c;可以根据需要自动选取要看的内容~ 本文目录&#xff1a;一、搭建4层全…

Spark核心RDD详解(设计与运行原理,分区,创建,转换,行动与持久化)

RDD设计背景与概念 在实际应用中&#xff0c;存在许多迭代式算法&#xff08;比如机器学习、图算法等&#xff09;和交互式数据挖掘工具&#xff0c;这些应用场景的共同之处是&#xff0c;不同计算阶段之间会重用中间结果&#xff0c;即一个阶段的输出结果会作为下一个阶段的输…

go: GOPATH entry is relative; must be absolute path: “F:oocode“.

系列文章目录 文章目录系列文章目录前言一、可以先查看一下啊二、gopath和goroot变量要和设置的一致总结前言 在安装hertz 之类的 总会弹出go 的不合法 等 出现这样的错误 要不就是go的不合法 会爆红 说go无这种命令 go&#xff1a;术语“ go”未被识别为cmdlet&#xff0c;函…

Hystrix断路器

目录 一、概述 &#xff08;一&#xff09;分布式系统面临的问题 &#xff08;二&#xff09;Hystrix是什么 &#xff08;三&#xff09;能干吗 &#xff08;四&#xff09;官网 &#xff08;五&#xff09;Hystrix官宣&#xff0c;停更进维 二、Hystrix重要概念 &…

JAVA开发(springBoot之HikariDataSource)

HikariDataSource是springBoot自带的数据源管理工具。应该是有zaxxer公司提供贡献给spring社区的。它是一款优秀的数据库连接池工具&#xff08;新的东西一般会吹吹牛&#xff09;&#xff0c;号称 Java WEB 当前速度最快的数据源&#xff0c;相比于传统的 C3P0 、DBCP、Tomcat…