图解排序算法之「冒泡排序」(详细解析)
1. 基本思想
冒泡排序(Bubble Sort)是最基础的排序算法之一,它的核心思想是:多次遍历要排序的序列,在遍历的过程中,当发现两个相邻的元素逆序,就交换这两个元素的位置,直到某次遍历不需要交换元素为止。此时整个序列都不存在两个元素逆序的情况,即满足了顺序要求。
为了让大家对冒泡排序有更加清晰的认识,我们以下面这组数据作为例子来演示冒泡排序:
现在,我们需要对包含 8 个元素的序列 [1, 9, 2, 6, 0, 8, 1, 7]
进行升序(从小到大)排序。
按照冒泡排序的思想,我们需要遍历这个序列,如果遍历的过程发现相邻元素中,左边的元素大于右边的元素时,就交换这两个元素的位置。如果是左边的元素小于所等于右边的元素时,满足从小到大的顺序要求,元素位置维持不变。下面就是一趟遍历的过程:
第一趟遍历下来,9 作为未排序区间中的最大元素,如同冒泡般上浮到了最右侧,形成了已排序区间的第一个元素。
我们下面接着第二趟:
走完第二趟之后,我们可以看到 8 作为未排序区间中的最大元素“上浮”到了未排序区间的最右侧,形成了已排序区间的第二个元素。
其实看到这里,大家都应该明白了,我们只要多再进行 7−27 - 27−2 趟遍历,就能将已排序区间扩大到整个序列,完成冒泡排序的过程,使得序列中所有的元素都是有序的。这便是冒泡排序的过程,如果你还不懂的话,推荐你自己在纸上手动模拟一遍。
听说还有人分不清楚选择排序和冒泡排序,这里简单说一下:选择排序的一次遍历仅是为了在未排序的区间中选出最值元素,然后放到已排序区间中;而冒泡排序的一次遍历是为了让较大的元素向未排序区间末端方向“上浮”,造成的结果不仅是最值元素被移到末端,还有过程中较大元素往“末端方向”移动,上面例子的第二趟遍历就很好地体现了这一过程。
2. 代码实现
冒泡排序的代码非常简单,直接上去就是两层 for 循环,给人一种暴力的直视感。外层循环控制遍历的趟数,内层循环控制控制每趟遍历访问区间的范围。if 条件句用来判断相邻元素是否逆序,里面的三行则是经典的元素交换语句。冒泡排序,一气呵成~
public void bubbleSort(int[] arr, int n) {//控制遍历的趟数for (int i = 0; i < n - 1; i++) {//控制遍历区间的范围for (int j = 0; j < n - i - 1; j++) {//判断是否逆序if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}
}
我们可以从上面代码得出,冒泡排序的时间复杂度为 O(n2)O(n^2)O(n2)。由于在两个元素相等的时候,并不会执行交换位置的操作,所以相等元素在排序前后相对顺序是不变的,即冒泡排序是一种稳定排序。
3. 优化
虽然说冒泡排序的最坏时间复杂度是 O(n2)O(n^2)O(n2),但是它的最佳时间复杂度应该是 O(n)O(n)O(n)。换句话说,上面提供的代码其实还有很大的优化空间。因为冒泡排序是一种基于比较的排序,我们在思考优化的时候,可以往减少比较次数的方向思考。
优化点1:其实这个优化点已经写在了开头的“基本思想”里面,我们算法只需要执行到某趟遍历不需要交换元素位置即可,因为此时所有相邻元素都满足顺序条件,不需要再继续遍历。
在代码层面,我们只需要加上一个标志变量,用于记录在这一趟遍历中是否发了元素交换,如果有则继续下一趟遍历,否则停止循环,完成排序。对应的优化版代码如下:
public void bubbleSort(int[] arr, int n) {for (int i = 0; i < n - 1; i++) {boolean isSorted = true; // #1 添加标记for (int j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;isSorted = false; // #2 发生了交换}}if (isSorted) {break; // #3 如果没有发生交换,则完成了排序}}
}
优化点2:这个优化点单纯通过思考有点难想到,需要对排序过程进行观察和模拟才会感受到。其实,每趟遍历的区间右边界都是由上一趟遍历最后一次发生交换的位置决定的。因为如果这个位置之后都没有发生交换,就说明之后的元素都是非逆序的,我们可以将有序区间的左边界直接扩展到最后一次发生交换的下一个位置。
在代码层面,我们可以将这两个优化点合并在一起,详情代码如下:
public void bubbleSort(int[] arr, int n) {int lastIndex = n - 1; // #1 有序区间的左边界for (int i = 0; i < n - 1; i++) {int tempIndex = lastIndex; // #2 标记// #3 遍历的右边界变成了lastIndexfor (int j = 0; j < lastIndex; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;tempIndex = j; // #4 记录发生交换的位置}}// #5 如果标记的值不变,则说明没有发生交换if (tempIndex == lastIndex) {break; } else {lastIndex = tempIndex;}}
}
至此,冒泡排序的优化就搞完了。因为在大多数时候我们排序的数据并不都是在极端情况,加了优化的冒泡排序能在很多时候比同样是 O(n2)O(n^2)O(n2) 的普通选择排序有更加优异的表现。
顺手更新一篇水文,希望你也可以顺手点赞呀~
图解排序算法之「冒泡排序」(详细解析)相关推荐
- 如果我问你:排序算法的「稳定性」有何意义?你怎么回答?
点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"加入公众号专属技术群 欢迎跳转到本文的原文链接:https://honeypps ...
- 如何看待 2020 届校招算法岗「爆炸」的情况?
编辑:忆臻 https://www.zhihu.com/question/342267611 本文仅作为学术分享,如果侵权,会删文处理 如何看待 2020 届校招算法岗「爆炸」的情况? 作者:Ted ...
- 图解排序算法(四)之归并排序
图解排序算法(四)之归并排序 基本思想 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide) ...
- 如何看待 2020 届校招算法岗「爆炸」的情况?英雄所见略同
来自:计算机视觉联盟公众号 转载 :知乎问题 如何看待 2020 届校招算法岗「爆炸」的情况? 链接:https://www.zhihu.com/question/342267611 本文仅作为学术交 ...
- Java十大排序算法总结,Java排序算法总结之冒泡排序
本文实例讲述了Java排序算法总结之冒泡排序.分享给大家供大家参考.具体分析如下: 前言:冒泡排序(BubbleSort)就是依次比较相邻的两个数,将小数放在前面,大数放在后面. 下面让我们一起 ...
- js排序算法详解-冒泡排序
全栈工程师开发手册 (作者:栾鹏) js系列教程5-数据结构和算法全解 js排序算法详解-冒泡排序 1.1 原始人冒泡排序 function bubbleSort(arr) {var len = ar ...
- 3.图解排序算法(三)之堆排序
作者: dreamcatcher-cx 出处: http://www.cnblogs.com/chengxiao/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在页面明显位 ...
- 图解排序算法之谈「选择排序」
1. 基本思想 选择排序(Select Sort)同样是最基础的排序算法之一,它的核心思想是:将要排序的序列分成有序和无序两个部分,开始时有序部分为空,然后经过 n - 1 次遍历,每次遍历都在无序部 ...
- mysql外部排序算法_「干货总结」程序员必知必会的十大排序算法
绪论 身为程序员,十大排序是是所有合格程序员所必备和掌握的,并且热门的算法比如快排.归并排序还可能问的比较细致,对算法性能和复杂度的掌握有要求.bigsai作为一个负责任的Java和数据结构与算法方向 ...
- 图解八大排序算法——我见过的最详细的讲解(转)
一.分类 1.内部排序和外部排序 内部排序:待排序记录存放在计算机随机存储器中(说简单点,就是内存)进行的排序过程. 外部排序:待排序记录的数量很大,以致于内存不能一次容纳全部记录,所以在排序过程中需 ...
最新文章
- Nginx反向代理负载均衡时,验证码不正确
- MySQL高级 —— 高性能索引
- jquery easyui datagrid js获取记录数 页数 当前页
- php 代码mysql 读写分离实例
- nginx配置错误页面
- 神经网络学习小记录59——Pytorch搭建常见分类网络平台(VGG16、MobileNetV2、ResNet50)
- 普通电脑用u盘安装服务器系统安装教程,Windows Server 2016使用U盘安装需要哪些步骤 硬盘安装Windows Server 2016图文教程...
- MFC ---- CString
- pip执行指令后报语法错误sys.stderr.write(f”ERROR: {exc}”)解决办法
- 概率统计Python计算:自定义离散型分布
- U8标准接口API生成采购到货单
- linux下设置定时执行脚本
- java官网以及java官网下载地址
- 状语从句到独立结构(absolute construction )
- Blender图解教程:刷权重技巧大全
- 用C++编写一个人事管理系统
- python地图匹配_地图匹配实践
- Excel锁定单元格【转帖】
- 易中天很有哲理的十句话
- 电缆卷筒滑环 电缆盘微型过孔导电滑环
热门文章
- 大学计算机信息技术教程电子书资源,教与学中用好教材《大学计算机信息技术教程》.pdf...
- 一篇文章带你了解jsMind
- VBA Excel 实践(三)Excel控件及Excel控件的初始化
- 大学生考华为认证有用吗?考研还是考证?这些困扰你许久的问题终于搞明白了
- Spring Bean的作用域
- TongLinkQ消息中间件使用(c语言)
- 线性混合效应模型Linear Mixed-Effects Models的部分折叠Gibbs采样
- 今日头条php笔试题,今日头条笔试题回顾及个人答案参考
- 硬盘数据恢复——误删除卷数据恢复
- 京瓷计算机无法打印机驱动程序,京瓷6525打印机驱动(修复京瓷6525打印机连接故障)V1.0 免费版...