常见排序算法及其稳定性分析

前言:

排序算法可以说是每一个程序员在学习数据结构和算法时必须要掌握的知识点,同样也是面试过程中可能会遇到的问题,在早些年甚至还会考冒泡排序。由此可见呢,掌握一些常见的排序算法是一个程序员的基本素养。虽然现在的语言标准库里都有直接的排序函数,但是作为一个学习者,我们应当抱着“知其然,还要知其所以然”的态度去学习。

1.常见的排序算法有哪些?

常见的排序算法及其性能:

算法名称平均时间复杂度稳定性
直接插入排序N^2稳定
希尔排序N^1.25--1.6N^1.25不稳定
选择排序N^2不稳定
堆排序NlogN不稳定
冒泡排序N^2稳定
快速排序NlogN不稳定
归并排序NlogN稳定

这里呢我只讲一些效率比较高的排序算法,比如快排、并排、堆排序、希尔排序。

2.常见排序算法的实现

2.1希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成grap个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序(插入排序)。然后,重复上述分组和排序的工作。当分组间距为1时,所有记录在统一组内排好序。

 ee378545516e471289b7abd8e82c1a73.png

代码实现:


// 希尔排序
void ShellSort(int* a, int n) {int gap = n - 1;//grap为分组之间的间隔while (gap > 1) {gap = gap / 3 + 1;//每次分组的间距都越来越小,直到间距为一for (int i = 0; i < gap; i++) {//i表示每组的开头位置for (int j = i; j < n - gap; j += gap) {//对每一组插入排序int end = j;int temp = a[j + gap];while (end >= 0) {if (temp < a[end]) {a[end + gap] = a[end];end -= gap;}else {break;}}a[end + gap] = temp;}}}
}

希尔排序的特性总结:

1. 希尔排序是对直接插入排序的优化

2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时(相当于对整个数组进行插入排序),数组已经接近有序的了,这样就 会很快。这样整体而言,可以达到优化的效果。

3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好多书中给出的 希尔排序的时间复杂度都不固定,目前也只能给出一个大概的复杂度。

2.2堆排序

关于堆排序在之前的博客中已经详细讲解过,有兴趣的可以去看看。

解决top-k问题--堆排序-CSDN博客文章浏览阅读140次,点赞8次,收藏5次。堆排序将堆的根节点与最后一个节点交换,然后将堆的大小减1,并进行堆的重构。max1>max2,a[i]>max2,所以此时的max2的值已经不是这个数组次大的了。被淘汰说明有前k个数大于自己,加入top-k说明此时的a[i]至少大于目前的maxk。,如果大于此时的maxk,说明就目前来说,a[i]有资格进入这前k大的数,通常的做法是用一个变量max1依次去比较数组中的每一个数,并更新。,它的每个节点的值都大于等于(或小于等于)它的子节点的值。先把此时的maxk的值去掉,算上a[i]之后调整这k个数。https://blog.csdn.net/qq_62987647/article/details/134765613

 2.3快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

 2.3.1递归版

对于单趟排序,我们确定一个基准值key,并利用双指针从首尾两端移动。找到一个位置放key,使得,key左边的元素都小于key(以升序排序为例),右边的元素都大于key

2031d1e64085469ba4845d2139ed7f10.png

快排的算法思想本质是用空间换取时间,每一次递归排序的区间都在不断地缩小,每一趟排序都确定了下一趟排序的左右两个区间,类似于二叉树的节点的左右儿子节点。 

78e8e47004294c32bb72bd941bd1755d.png

代码实现

int PartSort1(int* a, int left, int right) {//单趟排序int end = right;int begin = left;int mid = GetMid(a, left, right);//基准值的下标Swap(&a[left], &a[mid]);//跟首元素交换int key = left;while (begin < end) {while (end > begin && a[end] >= a[key])end--;while (end > begin && a[begin] <= a[key])begin++;Swap(&a[end], &a[begin]);}Swap(&a[key], &a[begin]);return begin;//单趟排序后返回最后key所处的位置
}
void QuickSort(int* a, int left, int right){//递归if (left >= right)return;int mid = PartSort3(a, left, right);QuickSort(a, left, mid - 1);QuickSort(a, mid + 1, right);
}

 2.3.2非递归版

利用,我们可以将单趟排序确定的两个子区间存起来,模拟函数栈帧的开辟。这样一来,我们就可以不用递归(递归较为耗损空间)就可以完成快速排序。

如果有对函数栈帧不了解的朋友可以去我之前写的博客里面看看。

深入理解函数调用--函数栈帧-CSDN博客文章浏览阅读87次,点赞6次,收藏3次。寄存器:寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。函数栈帧(Function Stack Frame)是在程序执行过程中,用来保存函数调用信息和局部变量的数据结构。它包含了函数的参数、返回地址和局部变量等信息。通俗的来说,就是在调用函数的时候系统给你开辟在栈区的一部分空间,专门用来存放局部变量等。https://blog.csdn.net/qq_62987647/article/details/132663764代码实现

void QuickSortNonR(int* a, int left, int right) {Stack ST;StackInit(&ST);StackPush(&ST, right);//根据栈的特性,先将右区间压栈StackPush(&ST, left);while (!StackEmpty(&ST)) {int l = StackTop(&ST);StackPop(&ST);int r = StackTop(&ST);StackPop(&ST);//取出一个区间[l,r]if (l > r)Swap(&l, &r);int keyi = PartSort1(a, l, r);//单趟排序if (keyi -1> l) {StackPush(&ST, keyi - 1);StackPush(&ST, l);}if (keyi + 1 < r) {StackPush(&ST, r);StackPush(&ST, keyi + 1);}}StackDestroy(&ST);
}

 2.4归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 与快排不同的是,归并排序是先递归后排序

实现思路: 

因为要先满足每个子序列都有序的条件,我们可以把区间长度划分为1,这样每个区间都一定是有序的,再对每个区间先上合并。值得注意的是,有序地子区间合并之后也是有序的。同样是根据二叉树的思想,每个区间一分为两个子区间,直到区间长度为1,需要logN的时间复杂度,合并两个区间又是N的复杂度,所以总的时间复杂度为NlogN

2a23d6c617044d108917794c823d362f.png

 代码实现

void MergeSort(int* a, int begin, int end, int* temp) {//temp用于暂时存放合并后的数组if (begin >= end)return;int mid = (begin + end) / 2;MergeSort(a, begin, mid, temp);MergeSort(a, mid + 1, end, temp);//先递归区间int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;//两个区间的端点指针int i = begin;//合并数组的下标的指针while (begin1 <= end1 && begin2 <= end2) {if (a[begin1] < a[begin2]) {//比较两个区间的首元素,小的放入temp中,两个指针往后移temp[i++] = a[begin1++];}else {temp[i++] = a[begin2++];}}//有可能还有某个区间还有元素没有放进去while (begin1 <= end1) {temp[i++] = a[begin1++];}while (begin2 <= end2) {temp[i++] = a[begin2++];}//将合并好的数组复制到原数组中memcpy(a + begin, temp + begin, sizeof(int) * (end - begin + 1));
}

3.排序算法的稳定性

对于某种排序算法,如果会将两个相同大小的元素的相对位置改变,那么我们就称这个算法是不稳定的,否者就是稳定的。 

1c9ad664ec9a4a08b2c92fc8bb88f6ae.png

什么时候需要考虑稳定性?

针对多个字段进行排序,就可能需要考虑排序算法的稳定性

 举例:

对以下数据进行排序:

序号订单金额订单时间
1509:04
2309:00
3509:03
4109:01

要求:

是按照订单金额进行升序排序,如果订单金额相同,则按照下单时间升序排序

先按照订单时间升序排序得到序号为:2、4、3、1

再从上一个序号组中按照订单金额升序排序

1.假如排序算法不稳定

则可能得到:4、2、1、3

对于序号1和3,订单金额相同,但是时间小的反而排在后面,不符合要求。

2.假如排序算法稳定

则一定得到:4、2、3、1

对于序号1和3,订单金额相同,下单时间大的排在后面,符合要求。

根据以上举例可以看出来,在对多个字段排序的时候,往往需要稳定的排序算法进行排序。

这也是为什么同样的时间复杂度,在有些时候能用不稳定的快排,有些时候用稳定归并排序。

 

 

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

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

相关文章

k8s的node亲和性和pod亲和性和反亲和性 污点 cordon drain

node亲和性和pod亲和性和反亲和性 污点 cordon drain 集群调度: schedule的调度算法 预算策略 过滤出合适的节点 优先策略 选择部署的节点 nodeName:硬匹配&#xff0c;不走调度策略&#xff0c;node01 nodeSelector:根据节点的标签选择&#xff0c;会走调度的算法 只…

PSoc62™开发板之PWM呼吸灯

实验目的 利用PWM动态调节输出功率达到控制LED呼吸变化的效果 实验准备 PSoc62™开发板&#xff08;开发板已经板载LED&#xff09; 板载资源 板载有多少pwm 创建工程例程&#xff0c;在libraries/HAL_Drivers/drv_pwm.h中查看BSP支持的pwm数量及对应的GPIO&#xff0c;可…

pgsql中epoch用法

问题描述 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 昨天又被叫回来加班,説是数据问题,又回来加班搞,到了以后发现数据没问题,那就是查询接口的事了,写查询接口的人用时间戳去查询,明明直接可以直接用日期查询,非得改成时间戳查询,结果还是有问题,接下来复盘一下…

centos安装gradle

1.将gradle.zip拷到centos 解压 2.配置环境变量 vim /etc/profile 在最后添加 export GRADLE_HOME/zx/gradle-8.5 export PATH$PATH:$GRADLE_HOME/bin:${PATH} 之后source /etc/profile gradle -version 安装成功

RK3566环境搭建

环境&#xff1a;vmware16&#xff0c;ubuntu 18.04 获取SDK前需要安装 sudo apt update sudo apt install -y repo git python 下载完成后先验证一下MD5码 md5sum rk356x_linux_release_v1.3.0b_20221213_split_dir/*firefly_split* 解压 rk3566ubuntu:/path/to$ mkdir ~…

【7-zip密码】7-Zip如何取消文件加密的密码

7z压缩包设置了密码&#xff0c;解压的时候就需要输入正确的密码才能顺利解压出文件&#xff0c;正常当我们解压文件或者删除密码的时候&#xff0c;虽然方法多&#xff0c;但是都需要输入正确的密码才能完成。忘记密码就无法进行操作。 那么&#xff0c;忘记了7z压缩包的密码…

Linux网络命令

文章目录 Linux网络网络配置命令1、ifconfig&#xff1a;查看网络接口信息&#xff08;显示所有活动网卡&#xff09;1.1 常用命令格式1.2 命令格式&#xff08;图文详解&#xff09;1.2.1 临时修改网卡名称1.2.2 永久修改网卡名称1.2.3 永久修改单个网卡 2、hostname&#xff…

JAVA面向对象基础-容器

一、泛型 我们可以在类的声明处增加泛型列表&#xff0c;如&#xff1a;<T,E,V>。 此处&#xff0c;字符可以是任何标识符&#xff0c;一般采用这3个字母。 【示例9-1】泛型类的声明 1 2 3 4 5 6 7 8 9 10 class MyCollection<E> {// E:表示泛型; Object[] o…

ESP32_ADC(Arduino)

ADC模数转换 ESP32集成了12位的逐次逼近式ADC&#xff0c;分别为ADC1模块ADC2模块&#xff0c;共支持18个模拟输入通道: ADC1模块&#xff1a;8个通道&#xff0c;32~39ADC2模块&#xff1a;10个通道&#xff0c;0&#xff0c;2&#xff0c;4&#xff0c;12 ~ 15&#xff0c;…

调试器加载错误,从任务栏打开可能会导致该问题 2024/1/8

&#x1f9e7;喜欢将常用软件固定在任务栏的用户肯定很苦恼这个问题 &#x1f9e7;问题复现 &#x1f9e7;这里先查找一下原因 &#x1f9e7;查看一下固定在任务栏的微信小程序开发工具的属性 如果不会打开任务栏图标属性界面的小伙伴请先翻到文章最后 &#x1f9e7;再使用同样…

CRM功能定制,哪些功能是需要格外注意的?

​到了2023年&#xff0c;在如今的商业环境中&#xff0c;千篇一律的方法很少能带来成功。这对于CRM管理系统尤其如此。虽然标准化的CRM解决方案为企业提供了一个简单的“入坑”门槛&#xff0c;但它们往往缺乏为企业带来真正竞争优势所需的灵活性&敏捷性。企业想要拥有适合…

【Python程序开发系列】一文总结API的基本概念、功能分类、认证方式、使用方法和开发流程

这是Python程序开发系列原创文章&#xff0c;我的第195篇原创文章。 一、什么是API&#xff1f; API是软件开发中非常重要的概念&#xff0c;它简化了不同组件之间的交互和集成&#xff0c;提供了对其他软件或服务功能的访问和调用方式。 API是应用程序编程接口&#xff08;Ap…

计算机配件杂谈-鼠标

目录 基础知识鼠标的发展鼠标的左右手鼠标的显示样式鼠标的移动和可见性移动可见性 现在的我们的生活工作都基本上离不开电脑了&#xff0c;不管是你平时玩玩游戏&#xff0c;上班工作等等&#xff1b; 今天将关于鼠标的一些小的技巧分享出来&#xff0c;共勉&#xff01; 基础…

Git删除远程仓库某次提交记录后的所有提交

1、鼠标右键->git bash here&#xff0c;然后cd切换到代码目录&#xff1b; 2、git log查看提交记录&#xff0c;获取commit id 3、git reset commit id&#xff08;commit id指要保留的最新的提交记录id&#xff09; 4、git push --force&#xff0c;强制push 如果出现…

上海亚商投顾:三大指数小幅反弹,旅游、机器人板块集体走强

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日震荡反弹&#xff0c;创业板指一度涨超1.7%&#xff0c;午后集体回落翻绿&#xff0c;临近尾盘又…

软考高级系统架构设计师考试经验分享

文章目录 1. 软考介绍&#xff08;1&#xff09;什么是软考&#xff08;2&#xff09;软考的作用&#xff08;3&#xff09;软考各科目的难度&#xff08;4&#xff09;考试时间&#xff08;5&#xff09;考试形式 2.系统架构设计师备考经验&#xff08;1&#xff09;辅导资料&…

知识点整理[(GraphGeo) DATA AND PROBLEM DEFINITION]

3 DATA AND PROBLEM DEFINITION 3.1 Data Collection 问题一:IP定位数据集构成 回答: 包含数以百万计的IP地址,这些IP地址包括: (1)它们具有自己的知识(如自主系统(AS)和WHOIS数据); (2)网络测量

pve多台物理机虚拟化 pve虚拟机优势

Proxmox VE是一个运行虚拟机和容器的平台。基于Debian Linux&#xff0c;完全开源。为了获得最大的灵活性&#xff0c;实现了两种虚拟化技术——基于内核的虚拟机(KVM)和基于容器的虚拟化(LXC)。一个主要的设计目标是使管理尽可能容易。运行在单个节点上使用Proxmox VE&#xf…

【低照度图像增强系列(3)】EnlightenGAN算法详解与代码实现

前言 ☀️ 在低照度场景下进行目标检测任务&#xff0c;常存在图像RGB特征信息少、提取特征困难、目标识别和定位精度低等问题&#xff0c;给检测带来一定的难度。 &#x1f33b;使用图像增强模块对原始图像进行画质提升&#xff0c;恢复各类图像信息&#xff0c;再使用目标检…

搜维尔科技:第九届元宇宙数字人设计大赛作品规范解读!

作品提交 参赛小组需要将作品上传至百度网盘&#xff0c;并将分享链接发送至frankaxis3d.cn邮箱。邮寄格式如下&#xff1a; 邮件标题&#xff1a;作品名称元宇宙数字人设计大赛作品 邮件内容标明&#xff1a;学校名称、院系名称、作品名称、作者名称、联系电话及指导老师名…