冒泡排序和选择排序区别_你以为只是简单的排序?(一)

一直在犹豫要不要写排序的文章,因为真的烂大街了。可是一旦细看,还真是很多值的思考的地方,所以还是选择记录一下

以下完整代码,均可从这里获取

https://github.com/Rain-Life/data-struct-by-go/tree/master/sort

排序算法效率分析

了解如何分析一个排序算法,可以帮助我们在实际工作场景中选择合适的排序算法,比如,如果排序的数据比较少,可以选择冒泡或插入排序,如果排序的数据量较大,选择归并或快速排序,虽然它们两两的时间复杂度是相同的,但是还是有很大的区别的,下边会对它们做对比

排序算法执行效率

一般分析一个排序算法的复杂度,我们都是去分析它的时间复杂度,时间复杂度反应的是数据规模n很大的时候的一个增长趋势。所以,通常在分析时间复杂度的时候会忽略到「系数、常数、低阶」。但是,在实际开发场景中,可能我们排序的数据并不多,因此,在对排序算法进行分析的时候,还是需要将系数、常数、低阶也考虑进来

分析一个排序算法的时间复杂度的时候,通常会分析它的「最好情况、最坏情况以及平均情况下的时间复杂度」。因为对于要排序的数据,它的有序度,对排序算法的执行时间是有影响的,所以,要想选择最合适的排序算法,这些情况的时间复杂度都应该考虑到(其实不光是排序,在实现任何一个算法的时候,当有多种方式可供选择的时候,都应该分析多重情况下的时间复杂度)

下边要分享的三个排序算法都是基于比较的排序算法,基于比较的排序算法的在执行过程中,一般涉及两种操作,一个是比较大小,一个是数据交换。因此,在对比这几种排序算法的时候,「比较次数和移动次数」也应该考虑进去。这也是为什么基于比较排序的算法,我们通常不会选择冒泡排序,而选择插入排序

排序算法内存消耗

「算法的内存消耗可以通过空间复杂度来衡量」。针对排序算法的空间复杂度,有一个新的概念是「原地排序」。原地排序算法,就是特指空间复杂度是O(1)的排序算法。下边要分享的三种排序算法,都是原地排序算法

排序算法稳定性

只靠执行效率和内存消耗来衡量排序算法的好坏是不够的。针对排序算法,还有一个重要的度量指标,「稳定性」。意思是,「如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变」

如:1、8、6、5、5、7、2、3,按照大小排序之后是:1、2、3、5、5、6、7、8

这组数据里有两个5。经过某种排序算法排序之后,如果两个5的前后顺序没有改变,那我们就把这种排序算法叫作「稳定的排序算法」;如果前后顺序发生变化,那对应的排序算法就叫作「不稳定的排序算法」

冒泡排序

冒泡排序优化

冒泡排序的实现思想,相信大家都非常的熟悉了

每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。「一次冒泡会让至少一个元素移动到它应该在的位置」,重复n次,就完成了n个数据的排序工作

实际上,冒泡过程还可以优化。当某次冒泡操作已经没有数据交换时,说明已经达到完全有序,不用再继续执行后续的冒泡操作。如图

188082f3368d8077c17071aee75168d6.png

下边是优化后的代码:

func BubbleSort(arr []int) {flag := falsen := len(arr)for i := 0; i < n; i++ {flag = false//如果某一次冒泡,没有出现数据交换,说明已经有序,不用再继续冒泡了for j := 0; j < n-i-1; j++ {if arr[j] > arr[j+1] {tmp := arr[j]arr[j] = arr[j+1]arr[j+1] = tmpflag = true}}if !flag {break}}for _, v := range arr {fmt.Printf("%vt", v)}
}

冒泡排序算法分析

首先「冒泡排序是一个原地排序算法」,因为冒泡排序只涉及相邻数据的交换,需要常量级的临时空间,所以空间复杂度是O(1)

在冒泡排序中,只有交换才可以改变两个元素的前后顺序。为了保证冒泡排序算法的稳定性,当有相邻的两个元素大小相等的时候,我们不做交换,相同大小的数据在排序前后不会改变顺序,所以「冒泡排序是稳定的排序算法」

在最好的情况下,也就是待排数据是完全有序的,那只需要进行一次冒泡操作即可,所以「最好情况下的时间复杂度是O(n)」

最坏情况下是待排数据是完全无序的,这个时候就需要n次冒泡,所以「最坏情况下的时间复杂度是O(n^2)」

平均情况下的时间复杂度的分析涉及比较复杂的推导,不是这里的重点(我也不会,手动狗头。如果你想了解,可以看这里),冒泡排序算法的平均时间复杂度是O(n^2)

插入排序

插入排序思想

插入排序是如何来的?假设现在有一个有序的数组,让你往里边插入一个数据之后,保持数组是有序的。我们都会想到通过遍历来找到待插入数据的位置,然后进行数据的移动。通过这种方式就可以保证这个数组有序。借鉴上边这种插入方法,于是就有了插入排序

插入排序的思想是:「将待排序的数组分成两个区间,有序区和无序区。刚开始的时候,有序区只有第一个元素。插入排序的过程就是每次从无序区中取出一个元素,放入到有序区中对应的位置,保证插入到有序区中之后,有序区依然是有序的。不断的重复这个过程,直到无序区为空」

文字描述比较抽象,见下图(从小到大排序)

da1ed9ae0769513070f21a8f932e09ae.png

插入排序也是包含两种操作,一种是比较,一种是移动。下边是代码实现

func InsertSort(arr []int)  {if len(arr) <= 0 {fmt.Println("待排数据不合法")}n := len(arr)for i := 1; i < n; i++ {//i是待排区的元素value := arr[i]j := i-1for ; j >= 0; j-- { //j遍历的是已排区的每一个元素if arr[j] > value {arr[j+1] = arr[j] //如果满足条件,将前一个值赋给后边这个} else {break}}arr[j+1] = value}for _, v := range arr {fmt.Printf("%vt", v)}
}

插入排序算法分析

插入排序也不需要额外的存储空间,空间复杂度是O(1),所以它是「原地排序算法」

在插入排序中,对于值相同的元素,我们可以选择将后面出现的元素,插入到前面出现元素的后面,这样就可以保持原有的前后顺序不变,所以插入排序是「稳定的排序算法」

如果待排序的数据是完全有序的,并不需要搬移任何数据。如果从尾到头在有序数据组里面查找插入位置,每次只需要比较一个数据就能确定插入的位置。所以这种情况下,最好是时间复杂度为O(n)。注意,「这里是从尾到头遍历已经有序的数据」

如果数组是倒序的,每次插入都相当于在数组的第一个位置插入新的数据,所以需要移动大量的数据,所以「最坏情况时间复杂度为O(n^2)」「平均时间复杂度也是O(n^2)」

选择排序

选择排序思想

选择排序的思想和插入排序的思想有些类似,选择排序是每次从无序区中选择一个最小的元素放入到有序区中,具体如图:

6e7d5fdc9f4bf21a1767944f1ea7bc71.png

代码实现如下

func SelectionSort(arr []int) {n := len(arr)if n <= 0 {fmt.Println("待排数据不合法")}for i := 0; i < n - 1; i++ {for j := i+1; j < n ; j++ {if arr[i] > arr[j] {arr[i],arr[j] = arr[j], arr[i]}}}for _, v := range arr {fmt.Printf("%vt", v)}
}

选择排序算法分析

选择排序的空间复杂度也是O(1),是原地排序算法。选择排序的最好情况和最坏情况的时间复杂度都是O(n^2),这个很简单,看一下它的执行过程就知道了

「选择排序不是一个稳定排序」,选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏了稳定性

比如7,3,5,7,1,9 这样一组数据,使用选择排序算法来排序的话,第一次找到最小元素1,与第一个7交换位置,那第一个7和中间的7顺序就变了,所以就不稳定了

这样一看,选择排序和前边两个排序算法相比就差一些了

三种排序算法对比

这里就先不提选择排序了,因为和前两种相比,它明显逊色一些

冒泡排序不管怎么优化,元素交换的次数是一个固定值。插入排序是同样的,不管怎么优化,元素移动的次数也是一个固定值。但是,从冒泡和插入排序的代码上看,冒泡排序的数据交换次数比插入排序要复杂,冒泡排序需要3个赋值操作,而插入排序只需要1个

冒泡排序的交换操作
if arr[j] > arr[j+1] {tmp := arr[j]arr[j] = arr[j+1]arr[j+1] = tmpflag = true
}选择排序的交换操作
if arr[j] > value {arr[j+1] = arr[j]
}

假设把一次赋值操作的时间算作一个单位时间,那冒泡排序的交换操作需要3个单位时间,而插入排序只需要1个单位时间

如果需要排序的数据比较少,可能这样的差别可以忽略不计,但是数据多起来之后,插入排序节省的时间还是比较明显的

3704b257da76bd4f7df97b79cec965a0.png

这三种时间复杂度为 O(n2) 的排序算法中,冒泡排序、选择排序,可能就纯粹停留在理论的层面了,学习的目的也只是为了开拓思维,实际开发中应用并不多,但是插入排序还是挺有用的。有些编程语言中的排序函数的实现原理会用到插入排序算法

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

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

相关文章

java hash 分库分表_分库分表方案

摘自&#xff1a;Java技术栈一、数据库瓶颈不管是IO瓶颈&#xff0c;还是CPU瓶颈&#xff0c;最终都会导致数据库的活跃连接数增加&#xff0c;进而逼近甚至达到数据库可承载活跃连接数的阈值。在业务Service来看就是&#xff0c;可用数据库连接少甚至无连接可用。接下来就可以…

verilog设计简易正弦波信号发生器_信号发生器工作原理是什么

信号发生器是指产生所需参数的电气测试信号的仪器。根据信号波形可分为四类&#xff1a;正弦信号、函数&#xff08;波形&#xff09;信号、脉冲信号和随机信号发生器。那么信号发生器工作原理是什么&#xff1f;听听广州荣鑫电子怎么说。信号发生器又称信号源或振荡器&#xf…

LeetCode 1780. 判断一个数字是否可以表示成三的幂的和(位运算)

文章目录1. 题目2. 解题1. 题目 给你一个整数 n &#xff0c;如果你可以将 n 表示成若干个不同的三的幂之和&#xff0c;请你返回 true &#xff0c;否则请返回 false 。 对于一个整数 y &#xff0c;如果存在整数 x 满足 y3xy 3^xy3x&#xff0c;我们称这个整数 y 是三的幂…

Android “再按一次退出“

1 Override2 public boolean onKeyDown(int keyCode, KeyEvent event) {3 if (keyCode KeyEvent.KEYCODE_BACK) 4 {5 if ((System.currentTimeMillis() - mExitTime) > 2000) { //第一种&#xff1a;判断2次按后退键间隔是否在2秒内6 …

python群控安卓_Github优秀项目推荐,安卓设备免Root实现低延迟投屏和远控

暑假找了个暑假工&#xff0c;没办法人总是要吃饭嘛&#xff0c;Weiney也不例外。亲戚家的小公司想做移动app引流&#xff0c;当然我是完全不懂什么引流&#xff0c;我也就做个打杂的工作。别看我技术一般&#xff0c;在这个不大的公司我还是唯一专业对口的技术人才呢&#xff…

.net pdf转图片_pdf2image类库实现批量pdf转图片

通过pdf2image来实现对PDF文件的处理工作&#xff0c;我们本次主要做的是将PDF文件批量转成图片。之前写过批量提取封面的文章&#xff0c;但是在后期的深入编写过程中遇到一些问题&#xff0c;近期再次深入编写程序&#xff0c;一起来看看代码吧&#xff01;python一、说明本次…

java 悬浮提示框_表格(悬浮框提示)

JTable的ToolTip提示和其它的组件提示是一样的,因为它们都是继承于Jcomponent,当我们需要为我们的单元格实现ToolTip的时候,只需要复写它的getToolTipText方法就可以了,看看Sun官方的例子&#xff1a;//Implement table cell tool tips.OverridepublicString getToolTipText(Mo…

linux远程工具_【linux实操3.1】linux远程连接工具Secure的使用

把自己的闲置笔记本用来做centos7服务器了&#xff0c;同时也出一期教程&#xff0c;记录自己同时也帮助需要的人&#xff1b;安排如下1、【linux实操1】华硕笔记本安装centos7实战2、【linux实操2】使用yum在命令行安装常用工具3、【linux实操3】安装漂亮的桌面xfce桌面**记录…

IOS--UIAlertView的使用方法详细

IOS--UIAlertView的使用方法详细 // UIAlertView的常用方法 // 标准样式 UIAlertView *oneAlertView [[UIAlertView alloc] initWithTitle:"标题"message:"提示内容" delegate:self cancelButtonTitle:"关闭"otherButtonTitles:"OK"…

afreecatv 回放下载_行车记录仪怎么看回放?行车记录仪停车后能自动录像吗

点击上面蓝色字↑↑↑即可免费订阅&#xff01;请喜欢的朋友请转发和分享&#xff0c;让更多朋友看到汽车知识天天学 (微信号&#xff1a;qiche92 )  行车记录仪最关键的一个部件&#xff0c;它就是储存零件——TF卡(内存卡)。在购买行车记录仪时&#xff0c;TF卡并不是标配&…

linux导出mysql下ssl证书_Linux系统下生成证书 https证书

平时都是用oneinstack加密https现在因为本地调试 需要https就找到这篇文章Linux系统下生成证书生成秘钥key,运行:$ openssl genrsa -des3 -out server.key 20481会有两次要求输入密码,输入同一个即可输入密码然后你就获得了一个server.key文件.以后使用此文件(通过openssl提供的…

lstm原始论文_有序的神经元——ON-LSTM模型浅析

尽管最近出现的Transformer系列的模型在nlp领域内很流行&#xff0c;但RNN仍然有着重要的地位。本文介绍的模型来自于ICLR 2019的最佳论文之一&#xff0c;它针对自然语言具有语法分层的特点&#xff0c;对原有的LSTM模型的结构做出了改进&#xff0c;使得新模型不仅具有更好的…

malloc函数详解

一、原型&#xff1a;extern void *malloc(unsigned int num_bytes); 头文件&#xff1a;#include <malloc.h> 或 #include <alloc.h> (注意&#xff1a;alloc.h 与 malloc.h 的内容是完全一致的。) 功能&#xff1a;分配长度为num_bytes字节的内存块 说明&#xf…

c++ 航空管理系统_浅谈航站楼能源管理系统的设计与应用

蒋超萍江苏安科瑞电器制造有限公司 江苏江阴 214400 【摘要】根据航站楼的特点&#xff0c;从航站楼能源消耗现状、能源管理系统的功能、系统架构、子系统以及能源的优化调度方案五个方面介绍了航站楼能源管理系统的设计&#xff0c;并对节能效果进行了预测。关键词&#xff1a…

mysql 线性表_线性表之顺序存储,基本操作

/*九大基本操作(不同的存储结构实现的代码不同)此处用一维数组的动态分配&#xff1a;InitList(&L);//初始化表&#xff0c;Length(L);LocateElem(L,e);GetElem(L,i);ListInsert(&L,i,e);ListDelete(&L,i,&e);PrintList(L);Empty(L);DestoryList(&L);*//*静…

mac python安装太慢_【已解决】Mac中给pip3添加代理以提升下载python包的速度

折腾&#xff1a;【未解决】Mac中Python 3.7安装TensorFlow期间&#xff0c;用&#xff1a;pip3 install tensorflow期间会去从下载python包此处连接files.pythonhosted.org的速度很慢。而自己有代理可用。所以想办法去给pip3或pip用上代理&#xff0c;加速下载mac pip3 use pr…

集合数据源

集合数据源主要包括ArrayList,Hashtabel,DataView,DataReader转载于:https://www.cnblogs.com/handsomer/p/4150400.html

LeetCode 1785. 构成特定和需要添加的最少元素(贪心)

文章目录1. 题目2. 解题1. 题目 给你一个整数数组 nums &#xff0c;和两个整数 limit 与 goal 。 数组 nums 有一条重要属性&#xff1a;abs(nums[i]) < limit 。 返回使数组元素总和等于 goal 所需要向数组中添加的 最少元素数量 &#xff0c;添加元素 不应改变 数组中 …

insert和update 锁等待_黑龙F5智感双全智能锁全球首发,掀起惊艳风潮

2020备受瞩目重磅新品 —— 黑龙F5智感双芯智能门锁&#xff0c;正式官宣&#xff0c;革新行业的智感解锁交互&#xff0c;强大的双芯片双智控&#xff0c;再次掀起惊艳风潮。双芯片科技感交互&#xff0c;领行业新风向黑龙F5智能锁前沿性地配备双芯片&#xff0c;以双核分别掌…

java喷泉编码_好程序员Java教程分享使用JS实现简单喷泉效果

原标题&#xff1a;好程序员Java教程分享使用JS实现简单喷泉效果好程序员Java教程分享使用JS实现简单喷泉效果&#xff0c;最近&#xff0c;在教学生使用JS的基本操作&#xff0c;为了练习JS的基本作用&#xff0c;特地写了一个喷泉效果&#xff0c;代码如下&#xff1a;页面代…