25.选择排序,归并排序,基数排序

news2024/9/22 9:58:07

目录

一. 选择排序

(1)简单选择排序

(2)堆排序

二. 归并排序

三. 基数排序

四. 各种排序方法的比较

(1)时间性能

(2)空间性能

(3)排序方法的稳定性能

(4)关于“排序方法的时间复杂度的下限”


一. 选择排序

(1)简单选择排序

基本思想:在待排序的数据中选出最大(小)的元素放在其最终的位置。
基本操作:
1.首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换。
2.再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换。
3.重复上述操作,共进行n-1趟排序后,排序结束。

 

 不难写出算法:

void SelectSort(SqList &L){
    for(i=1; i<L.length; ++i){
        k=i;  //第i趟从第i个元素开始
        for(j=i+1; j<=L.length; j++)
            if(L.r[j].key < L.r[k].key) k=j;  //记录最小值位置
        if(k!=i)  L.r[i]←—→L.r[k];  //交换
    }
}

下面我们分析时间复杂度。对移动次数来说,最好情况是0,最坏情况是3(n-1),也就是每一趟都得移动(每次移动需要移动3次)。对比较次数来说,无论待排序列处于什么状态,选择排序所需进行的"比较”次数都相同,为\sum_{i=1}^{n-1}(n-i)=\frac{n}{2}(n-1)

上面的算法是不稳定排序(但是可以稳定化)。具体的说用数组实现的选择排序是不稳定的,用链表实现的选择排序是稳定的。例如,给定8,5,8*,7,9;第1次:5,8,8*,7,9;第2次:5,7,8*,8,9;从而可以验证它是不稳定的。

(2)堆排序

堆的定义:若n个元素的序列{\left \{ a_1,a_2...a_n \right \}}满足\left\{\begin{matrix} a_i\leqslant a_{2i}\\ a_i\leqslant a_{2i+1} \end{matrix}\right.\left\{\begin{matrix} a_i\geqslant a_{2i}\\ a_i\geqslant a_{2i+1} \end{matrix}\right.,则分别称该序列为小根堆和大根堆。从堆的定义可以看出,堆实质是满足如下性质的完全二叉树:二叉树中任一非叶子结点均小于(大于)它的孩子结点。

显然,大根堆的根结点是最大值,小根堆的根结点是最小值。若在输出堆顶的最小值(最大值)后,使得剩余n-1个元素的序列又重建成一个堆,则得到n个元素的次小值(次大值)....如此反复,便能得到一个有序序列,这个过程称之为堆排序

那么怎么重建呢?以小(大)根堆为例:
1.输出堆顶元素之后,以堆中最后一个元素(编号最大的元素)替代之;
2.然后将根结点值与左、右子树的根结点值进行比较,并与其中小(大)者进行交换;
3.重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”。

例如,对下面的小根堆,把13输出,最后一个元素97作为根结点,它的左右孩子是38和27,27较小,所以把97和27交换。此时97的左右孩子是65和49,49较小,把49和97交换,这个时候97已经是叶子结点就不用再操作了。

 写出算法如下:

void HeapAdjust(elem R[], int s, int m){
/*已知R[s..m]中记录的关键字除R[s]之外均满足堆的定义,本函数调整R[s]的关键字,使R[s..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;  //rc大于左右孩子,这个时候已经符合要求,就不用做了
        R[s] = R[j];  //较大的孩子结点往上升
        s = j;  //rc应插入在位置s上,更新s
    }//for
    R[s] = rc;  //插入
}//HeapAdjust

HeapAdjust函数是一个用于调整堆的函数。它接受一个数组R,以及两个整数s和m作为参数。s表示要调整的子树的根节点的位置,m表示该子树的最后一个节点的位置。

首先,将根节点的值保存在变量rc中。然后,通过一个循环来比较根节点和其子节点的值。在循环中,变量j初始化为根节点的左子节点的位置(2*s),然后每次乘以2,即可得到下一个子节点的位置。在循环中,首先判断是否存在右子节点,并且右子节点的值是否大于左子节点的值。如果满足条件,则将j加1,即将j指向右子节点。然后,判断rc的值是否大于等于R[j]的值。如果满足条件,则退出循环。如果rc的值小于R[j]的值,则将R[j]的值赋给R[s],即将较大的子节点的值上移到根节点的位置。然后,将s更新为j,即将s指向较大子节点的位置。循环结束后,将rc的值赋给R[s],即将根节点的值放到合适的位置上。这样,HeapAdjust函数完成了对以s为根节点的子树的调整,使其满足堆的性质。

可以看出:对一个无序序列反复“筛选”就可以得到一个堆。即:从一个无序序列建堆的过程就是一个反复“筛选”的过程。我们重新考察堆的定义,显然:单结点的二叉树是堆,在完全二叉树中所有以叶子结点(序号i > n/2,这里是整除向下取整)为根的子树也是堆。这样,我们只需依次将以序号为n/2,n/2 - 1,.....1的结点为根的子树均调整为堆即可。即:对应由n个元素组成的无序序列,“筛选”只需从第n/2个元素开始。

由于堆实质上是一个线形表,那么我们可以顺序存储一个堆。下面以一个实例介绍建一个小根堆的过程。例如给定关键字为49,38,65,97,76,13,27,49的一组记录,将其按关键字调整为一个小根堆:

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

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

上面我们了解了怎么建堆。若对一个无序存列建堆,然后输出根。重复该过程就可以由一个无需序列输出有序序列。实质上,堆排序就是利用完全二叉树中父结点与孩子结点之间的内在关系来排序的。

void HeapSort(elem R[]){  //对R[1]到R[n]进行堆排序
    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, 1, i-1);  //对R[1]到R[i-1]重新建堆}
}//HeapSort

最后我们来研究时间复杂度。初始堆化所需时间不超过O(n),排序阶段(不含初始堆化)每次重新堆化所需时间不超过O(logn),则n-1次循环所需时间不超过O(nlogn)。因此:
Tw(n)=O(n)+ O(nlogn)= O(nlogn)

堆排序的时间主要耗费在建初始堆和调整建新堆时进行的反复筛选上。堆排序在最坏情况下,其时间复杂度也为O(nlog2n),这是堆排序的最大优点。无论待排序列中的记录是正序还是逆序排列,都不会使堆排序处于"最好"或"最坏"的状态。另外,堆排序仅需一个记录大小供交换用的辅助存储空间。

然而堆排序是一种不稳定的排序方法,它不适用于待排序记录个数n较少的情况,但对于n较大的文件还是很有效的。

二. 归并排序

基本思想:将两个或两个以上的有序子序列“归并”为一个有序予列。在内部排序中,通常采用的是2-路归并排序。即:将两个位置相邻的有序子序列R[1..m]和R[m+1..n]归并为一个有序序列R[1..n]。

这种树称为归并树。n个元素归并排序只需要\left \lceil log_2n \right \rceil趟。下面讨论怎么把两个有序序列合并成一个有序序列。这里可以参考线性表的合并算法。设R[low]-R[mid]和R[mid+1]-R[high]为相邻,归并成一个有序序列R1[low] - R1[high].

若SR[i].key<=SR[j].key,则TR[k]=RS[i];k++;i++;  否则,TR[k]=SR[j];k++;j++;

归并排序的时间效率是O(nlog2n),空间效率是O(n),因为需要一个与原始序列同样大小的辅助序列(TR)。这正是此算法的缺点。归并排序算法是稳定的算法。

三. 基数排序

基本思想:分配+收集

基数排序也叫桶排序或箱排序:设置若干个箱子,将关键字为k的记录放入第k个箱子,然后在按序号将非空的连接。基数排序的数字是有范围的,均由0-9这十个数字组成,则只需设置十个箱子,相继按个、十、百...进行排序。例:给定待排序序列(614,738,921,485,637,101,215,530,790,306)。这里每一个箱子都是一个队列,遵循先进先出的原则:

至此排序完成!基数排序的时间效率:O(k*(n+m)),其中k:关键字个数(上面有3个关键字),m:关键字取值范围为m个值(上面为10),n:元素个数。这里,每一趟分配n个元素,收集m个桶,总共需要k遍。

空间效率:这里需要放置m个桶,回收的时候回收n个元素,则空间复杂度是O(n+m)。基数排序是稳定的。

四. 各种排序方法的比较

(1)时间性能

1.按平均的时间性能来分,有三类排序方法:

  • 时间复杂度为O(nlogn)的方法有:快速排序、堆排序和归并排序,其中以快速排序为最好;
  • 时间复杂度为O(n^2)的有:直接插入排序、冒泡排序和简单选择排序,其中以直接插入为最好,特别是对那些对关键字近似有序的记录序列尤为如此;
  • 时间复杂度为O(n)的排序方法只有:基数排序。

2.当待排记录序列按关键字顺序有序时,直接插入排序和冒泡排序能达到到O(n)的时间复杂度;而对于快速排序而言,这是最不好的情况,此时的时间性能退化为O(n^2),因此是应该尽量避免的情况。
3.简单选择排序、堆排序和归并排序的时间性能不随记录序列中关键字的分布而改变。

(2)空间性能

指的是排序过程中所需的辅助空间大小.
1.所有的简单排序方法(包括:直接插入、冒泡和简单选择)和堆排序的空间复杂度为O(1)
2.快速排序为O(logn),为栈所需的辅助空间
3.归并排序所需辅助空间最多,其空间复杂度为O(n)
4.链式基数排序需附设队列首尾指针,则空间复杂度为O(rd)

(3)排序方法的稳定性能

稳定的排序方法指的是,对于两个关键字相等的记录,它们在序列中的相对位置,在排序之前和经过排序之后,没有改变。

  • 当对多关键字的记录序列进行LSD方法排序时,必须采用稳定的排序方法。
  • 对于不稳定的排序方法,只要能举出一个实例说明即可。
  • 快速排序和堆排序是不稳定的排序方法。

(4)关于“排序方法的时间复杂度的下限”

本章讨论的各种排序方法,除基数排序外,其它方法都是基于“比较关键字”进行排序的排序方法,可以证明,这类排序法可能达到的最快的时间复杂度为O(nlogn)。(基数排序不是基于“比较关键字”的排序方法,所以它不受这个限制)。

可以用一棵判定树来描述这类基于“比较关键字”进行排序的排序方法。

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

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

相关文章

港联证券|股票过户费是什么意思?

股票过户费是指在股票商场中&#xff0c;由于股份所有权的转让&#xff0c;双方需求付出的一种买卖费用。这种费用首要是为了付出证券公司和证券中介机构转让股票所发生的各项费用&#xff0c;如代理费、登记费、买卖税等。股票过户费的数额一般是按照股票的数量和买卖金额来核…

ffmpeg 配合Fiddler抓包获取视频操作

一&#xff0e;获取普通网站视频 1.安装Fiddler软件&#xff0c;直接点击绿色软件中Fiddler.exe&#xff0c;打开即可 2.打开后需要设置一下https解码 3.打开普通视频&#xff0c;获取视频链接在网页打开即可 二&#xff0e;获取一级反爬网站视频 1.随便找一个video/mp…

Redis主从复制的搭建及原理分析

目录 一、Redis主从复制 1.1 搭建主从复制架构 1.1.1 主从复制架构简介 1.1.2 搭建主从复制架构 1.2 主从复制工作流程 1.2.1 建立连接阶段 1.2.2 数据同步阶段 1.2.2.1 工作流程 1.2.2.2 增量同步原理 1.2.3 命令传播阶段 1.2.3.1 偏移量&#xff08;offset&#xf…

固定资产太多怎么管理好

固定资产太多时&#xff0c;可以采取以下措施进行管理&#xff1a;  分类管理&#xff1a;将固定资产按照种类、用途等进行分类&#xff0c;便于管理和查询。  建立台账&#xff1a;建立固定资产台账&#xff0c;记录每项资产的名称、编号、购置日期、购买价格、使用部门、…

keil在点击debug无法运行(全速运行)

1、今天发现我之前可以debug的程序&#xff0c;在板子上无法debug了&#xff0c;打断点完全没用 2、换了电脑&#xff0c;带板子过去也这样&#xff0c;之前可以运行的代码都debug不了 3、按照网上的方法&#xff0c;都不行&#xff0c;全速运行&#xff0c;单步执行都是灰色…

nodejs pkg打包生成exe,设置自定义图标和产品信息

一、使用node开发应用程序,通常采用express框架进行功能扩展,当系统开发完成后,直接的方式就是采用gulp压缩后,在服务端运行node app.js命令执行,to C的系统这样开发部署没有问题。 二、而在to B,to G的项目中,采用源码的方式运行,一方面部署不太友好,需要拷贝的文件很…

Python随机森林、线性回归对COVID-19疫情、汇率数据预测死亡率、病例数、失业率影响可视化...

全文链接&#xff1a;https://tecdat.cn/?p33536 自2019年12月以来&#xff0c;传染性冠状病毒疾病2019&#xff08;COVID-19&#xff09;迅速席卷全球&#xff0c;并在短短几个月内达到了大流行状态&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 相关视频…

双基证券:中国房地产开发投资可能已经见底 未来或将反弹

双基证券表示&#xff0c;差别化住宅信贷方针优化调整&#xff0c;估计将和前期认房不认贷的方针一起构成合力&#xff0c;助力居民减轻置业担负&#xff0c;有利于加快开释合理住宅需求。这也显示了方针积极有为&#xff0c;决断致力于防危险和稳增长。存量房贷利率重定价&…

sort排序字母+数字混合排序

使用ocaleCompare进行排序后&#xff0c;发现排序后的顺序是这样的&#xff08;为什么会这样排序&#xff0c;可以自己去研究一下&#xff09;&#xff0c;但这并不是自己想要的。期望顺序&#xff1a;A-11&#xff0c;A-22&#xff0c;A-111&#xff0c;A-222 在网上查找了半…

企业架构LNMP学习笔记2

企业架构分布式集群最终解决方案 集群&#xff1a;多台服务器在一起做同样的事情。 分布式&#xff1a;多台服务器在一起做不同的事情。 最终架构&#xff1a;实现负载均衡LB&#xff0c;高可用HA&#xff0c;数据库主从复制M-S&#xff0c;读写分离R-W&#xff0c;缓存中间件…

火热的低代码

低代码风头正紧&#xff0c;不管你是做后端开发、还是前端设计、销售、售前&#xff0c;如果你没接触过低代码都不好意思说自己在软件领域工作&#xff0c;这篇文章从我的角度聊聊低代码是什么、以怎么样的方式开发、及低代码的未来发展趋势。 一、低代码由来 低代码并不是一项…

红石外汇|每日汇评:如非农数据疲软的情况下,黄金是否会突破1955美元的100日移动平均线?

1、金价已进入 1,950 美元下方盘整阶段&#xff0c;关注美国非农就业数据&#xff1b; 2、在关键就业数据和美国劳工节长周末之前&#xff0c;美元出现空头回补&#xff1b; 3、9月份开始&#xff0c;金价关注 100 日移动平均线 1955 美元&#xff1b; 金价交易接近一个月高点…

记一次反弹shell的操作【非常简单】

#什么是反弹shell 通常我们对一个开启了80端口的服务器进行访问时&#xff0c;就会建立起与服务器Web服务链接&#xff0c;从而获取到服务器相应的Web服务。而反弹shell是我们开启一个端口进行监听&#xff0c;转而让服务器主动反弹一个shell来连接我们的主机&#xff0c;我们再…

Python2022年06月Python二级 -- 编程题解析

题目一: 学过编程的小程同学想帮助医生对核酸检测人群进行分流和统计&#xff0c;根据健康码的颜色分配不同的核酸检测区域: 红码:A区 橙码:B区 绿码:C区 等待核酸检测人数众多&#xff0c;但是具体检测人数未知&#xff0c;请你帮小程编写一个程序来协助医生对核酸人群进行人群…

如何创建HttpServletRequest对象

我们常用的就是在Controller层的接口入参时定义&#xff0c;这样我们就能直接用了&#xff0c;如下图&#xff1a; 但是某些情况&#xff0c;我们需要传递这个request 到各种工具类中&#xff0c;传递这个request 相对要麻烦一些&#xff0c; 我们可以不用传递&#xff0c;在…

工具类APP如何解决黏性差、停留短、打开率低等痛点?

工具产品除了需要把自己的功能做到极致之外&#xff0c;其实需要借助一些情感手段、增设一些游戏机制、输出高质量内容、搭建社区组建用户关系链等方式&#xff0c;来提高产品的用户黏性&#xff0c;衍生产品的价值链。 工具类产品由于进入门槛低&#xff0c;竞争尤为激烈&…

【UIPickerView案例03-点餐系统之随机点餐 Objective-C语言】

一、先来看看我们这个示例程序里面,随机点餐是怎么做的 1.点击:“随机点餐”按钮 大家能想到,它是怎么实现的吗 1)首先,点击”随机点餐“按钮,的时候,你要让这个pickerView,进行随机选中,那么,得监听它的点击 2)然后呢,让pickeView选中数据, 3)然后呢,把那个…

IDEA无效发行版本17

IDEA无效发行版本17 idea开发工具依赖的 jdk版本 和 项目依赖的jdk版本一定要保持 一致&#xff0c;不然会报错。 setting-->build-->compiler-》javaCompiler project->structure 这个也要保持一样。 在porm.xml文件中&#xff0c;你配置jdk版本是1.8&#xff0c;这…

【云原生】Kubernetes容器编排工具

目录 1. K8S介绍 1.1 k8s的由来 下载地址 1.2 docker编排与k8s编排相比 1.3 传统后端部署与k8s 的对比 传统部署 k8s部署 ​2. k8s的集群架构与组件 &#xff08;1&#xff09; Kube-apiserver &#xff08;2&#xff09;Kube-controller-manager &#xff08;3&a…

怎样将几个pdf合并?

在日常工作中&#xff0c;我们经常需要处理大量的PDF文件。有时候&#xff0c;我们需要将多个PDF文件合并成一个文件&#xff0c;以便于快速传输或方便查阅。虽然PDF文件本身不能进行编辑&#xff0c;但是借助专业的PDF编辑软件&#xff0c;我们可以轻松地实现将多个PDF文件合并…