漫画:什么是冒泡排序

转载自   漫画:什么是冒泡排序

什么是冒泡排序?

冒泡排序的英文Bubble Sort,是一种最基础的交换排序

大家一定都喝过汽水,汽水中常常有许多小小的气泡,哗啦哗啦飘到上面来。这是因为组成小气泡的二氧化碳比水要轻,所以小气泡可以一点一点向上浮动。

 

而我们的冒泡排序之所以叫做冒泡排序,正是因为这种排序算法的每一个元素都可以像小气泡一样,根据自身大小,一点一点向着数组的一侧移动。

具体如何来移动呢?让我们来看一个栗子:

 

有8个数组成一个无序数列:5,8,6,3,9,2,1,7,希望从小到大排序。

按照冒泡排序的思想,我们要把相邻的元素两两比较,根据大小来交换元素的位置,过程如下:

首先让5和8比较,发现5比8要小,因此元素位置不变。

接下来让8和6比较,发现8比6要大,所以8和6交换位置。

 

 

继续让8和3比较,发现8比3要大,所以8和3交换位置。

 

 

 

继续让8和9比较,发现8比9要小,所以元素位置不变。

接下来让9和2比较,发现9比2要大,所以9和2交换位置。

 

 

接下来让9和1比较,发现9比1要大,所以9和1交换位置。

 

 

最后让9和7比较,发现9比7要大,所以9和7交换位置。

 

 

这样一来,元素9作为数列的最大元素,就像是汽水里的小气泡一样漂啊漂,漂到了最右侧。

这时候,我们的冒泡排序的第一轮结束了。数列最右侧的元素9可以认为是一个有序区域,有序区域目前只有一个元素。

 

下面,让我们来进行第二轮排序:

首先让5和6比较,发现5比6要小,因此元素位置不变。

接下来让6和3比较,发现6比3要大,所以6和3交换位置。

 

 

继续让6和8比较,发现6比8要小,因此元素位置不变。

接下来让8和2比较,发现8比2要大,所以8和2交换位置。

 

 

接下来让8和1比较,发现8比1要大,所以8和1交换位置。

 

 

继续让8和7比较,发现8比7要大,所以8和7交换位置。

 

第二轮排序结束后,我们数列右侧的有序区有了两个元素,顺序如下:

 

至于后续的交换细节,我们这里就不详细描述了,第三轮过后的状态如下:

 

第四轮过后状态如下:

 

第五轮过后状态如下:

 

第六轮过后状态如下:

 

第七轮过后状态如下(已经是有序了,所以没有改变):

 

第八轮过后状态如下(同样没有改变):

 

到此为止,所有元素都是有序的了,这就是冒泡排序的整体思路。

原始的冒泡排序是稳定排序。由于该排序算法的每一轮要遍历所有元素,轮转的次数和元素数量相当,所以时间复杂度是O(N^2) 

 

冒泡排序第一版:

public class BubbleSort {

  1. private static void sort(int array[])

  2. {

  3.    int tmp  = 0;

  4.    
       
    for(int i = 0; i < array.length; i++){

  5.        for(int j = 0; j < array.length - i - 1; j++)

  6.        {

  7.            if(array[j] > array[j+1])

  8.            {

  9.                tmp = array[j];

  10.                array[j] = array[j+1];

  11.                array[j+1] = tmp;

  12.            }

  13.        }

  14.    }

  15. }

  16.  

  17. public static void main(String[] args){

  18.    int[] array = new int[]{5,8,6,3,9,2,1,7};

  19.    sort(array);

  20.    System.out.println(Arrays.toString(array));

  21. }

}

代码非常简单,使用双循环来进行排序。外部循环控制所有的回合,内部循环代表每一轮的冒泡处理,先进行元素比较,再进行元素交换。

 

 

 

 

 

————————————

 

 

 

 

原始的冒泡排序有哪些优化点呢?

让我们回顾一下刚才描述的排序细节,仍然以5,8,6,3,9,2,1,7这个数列为例,当排序算法分别执行到第六、第七、第八轮的时候,数列状态如下:

 

很明显可以看出,自从经过第六轮排序,整个数列已然是有序的了。可是我们的排序算法仍然“兢兢业业”地继续执行第七轮、第八轮。

这种情况下,如果我们能判断出数列已经有序,并且做出标记,剩下的几轮排序就可以不必执行,提早结束工作。

 

冒泡排序第二版

public class BubbleSort {

  1. private static void sort(int array[])

  2. {

  3.     int tmp  = 0;

  4.    for(int i = 0; i < array.length; i++)

  5.    {

  6.        //有序标记,每一轮的初始是true

  7.        boolean isSorted = true;

  8.        for(int j = 0; j < array.length - i - 1; j++)

  9.        {

  10.            if(array[j] > array[j+1])

  11.            {

  12.                tmp = array[j];

  13.                array[j] = array[j+1];

  14.                array[j+1] = tmp;

  15.                //有元素交换,所以不是有序,标记变为false

  16.                isSorted = false;

  17.            }

  18.        }

  19.        if(isSorted){

  20.            break;

  21.        }

  22.    }

  23. }

  24.  

  25. public static void main(String[] args){

  26.    int[] array = new int[]{5,8,6,3,9,2,1,7};

  27.    sort(array);

  28.    System.out.println(Arrays.toString(array));

  29. }

}
这一版代码做了小小的改动,利用布尔变量isSorted作为标记。如果在本轮排序中,元素有交换,则说明数列无序;如果没有元素交换,说明数列已然有序,直接跳出大循环。

 

 

为了说明问题,咱们这次找一个新的数列:

 

这个数列的特点是前半部分(3,4,2,1)无序,后半部分(5,6,7,8)升序,并且后半部分的元素已经是数列最大值。

让我们按照冒泡排序的思路来进行排序,看一看具体效果:

第一轮

元素3和4比较,发现3小于4,所以位置不变。

元素4和2比较,发现4大于2,所以4和2交换。

 

 

元素4和1比较,发现4大于1,所以4和1交换。

 

 

元素4和5比较,发现4小于5,所以位置不变。

元素5和6比较,发现5小于6,所以位置不变。

元素6和7比较,发现6小于7,所以位置不变。

元素7和8比较,发现7小于8,所以位置不变。

第一轮结束,数列有序区包含一个元素:

 

 

第二轮

元素3和2比较,发现3大于2,所以3和2交换。

 

 

元素3和1比较,发现3大于1,所以3和1交换。

 

 

元素3和4比较,发现3小于4,所以位置不变。

元素4和5比较,发现4小于5,所以位置不变。

元素5和6比较,发现5小于6,所以位置不变。

元素6和7比较,发现6小于7,所以位置不变。

元素7和8比较,发现7小于8,所以位置不变。

第二轮结束,数列有序区包含一个元素:

 

 

 

 

这个问题的关键点在哪里呢?关键在于对数列有序区的界定。

按照现有的逻辑,有序区的长度和排序的轮数是相等的。比如第一轮排序过后的有序区长度是1,第二轮排序过后的有序区长度是2 ......

实际上,数列真正的有序区可能会大于这个长度,比如例子中仅仅第二轮,后面5个元素实际都已经属于有序区。因此后面的许多次元素比较是没有意义的。

如何避免这种情况呢?我们可以在每一轮排序的最后,记录下最后一次元素交换的位置,那个位置也就是无序数列的边界,再往后就是有序区了。

 

冒泡排序第三版

public class BubbleSort {

  1. private static void sort(int array[])

  2. {

  3.    int tmp  = 0;

  4.    //记录最后一次交换的位置

  5.    int lastExchangeIndex = 0;

  6.    //无序数列的边界,每次比较只需要比到这里为止

  7.    int sortBorder = array.length - 1;

  8.    for(int i = 0; i < array.length; i++)

  9.    {

  10.        //有序标记,每一轮的初始是true

  11.        boolean isSorted = true;

  12.  

  13.        for(int j = 0; j < sortBorder; j++)

  14.        {

  15.            if(array[j] > array[j+1])

  16.            {

  17.                tmp = array[j];

  18.                array[j] = array[j+1];

  19.                array[j+1] = tmp;

  20.                //有元素交换,所以不是有序,标记变为false

  21.                isSorted = false;

  22.                //把无序数列的边界更新为最后一次交换元素的位置

  23.                lastExchangeIndex = j;

  24.            }

  25.        }

  26.        sortBorder = lastExchangeIndex;

  27.        if(isSorted){

  28.            break;

  29.        }

  30.    }

  31. }

  32.  

  33.  

  34. public static void main(String[] args){

  35.    int[] array = new int[]{3,4,2,1,5,6,7,8};

  36.    sort(array);

  37.    System.out.println(Arrays.toString(array));

  38. }

}

这一版代码中,sortBorder就是无序数列的边界。每一轮排序过程中,sortBorder之后的元素就完全不需要比较了,肯定是有序的。

 

 

 

几点补充:

 

本漫画纯属娱乐,还请大家尽量珍惜当下的工作,切勿模仿小灰的行为哦。

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

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

相关文章

CentOS - 修改主机名教程(将 localhost.localdomain 改成其它名字)

https://www.cnblogs.com/gudi/p/7846978.html 需要关闭防火墙&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; Linux修改主机名称 碰到这个问题的时候&#xff0c;是在安装Zookeeper集群的时候&#xff0c;碰到如下问题 java.net.U…

使用JDBCTemplate实现与Spring结合,方法公用

该篇博文主要是体现了接口和类的公用性&#xff0c; Emp的实体类&#xff1a; package org.entity;import java.util.Date;/*** Emp entity. author MyEclipse Persistence Tools*/public class Emp implements java.io.Serializable {// Fieldsprivate Integer empno;private…

.net core 源码解析-web app是如何启动并接收处理请求(二) kestrel的启动

上篇讲到.net core web app是如何启动并接受请求的&#xff0c;下面接着探索kestrel server是如何完成此任务的。 1.kestrel server的入口KestrelServer.Start (Microsoft.AspNetCore.Hosting.Server.IHttpApplication ) FrameFactory创建的frame实例最终会交给libuv的loop回调…

MySQL中的any_value()函数

https://blog.csdn.net/u014079773/article/details/93722761 https://www.thinbug.com/q/37089347 https://blog.csdn.net/Peacock__/article/details/90608246 https://www.itranslater.com/qa/details/2109775246877262848 4.any_value()会选择被分到同一组的数据里…

MybatisPlus使用

Mybatisplus 导入依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</gro…

面试为什么需要了解JVM

转载自 面试为什么需要了解JVM 说在前面 如果你经常注意面试题&#xff0c;你会发现现在面试题多多少少会含有jvm相关的面试题&#xff0c;那么为什么现在面试需要了解或者问面试题呢&#xff1f; 主题 谈谈自己的理解&#xff0c;概括为以下几个方面&#xff1a; 的确很重…

使用JDBCTemplate实现与Spring结合,方法公用 ——接口(BaseDao)

/** * Title: BaseDao.java * Package org.dao * Description: TODO该方法的主要作用&#xff1a; * author A18ccms A18ccms_gmail_com * date 2017-6-3 下午2:40:13 * version V1.0 */ package org.dao;import java.io.Serializable; import java.util.List;/** * …

Centos7安装apt-get 在centos下用yum install xxx        不是使用apt-get

https://www.cnblogs.com/yadongliang/p/8660046.html centos中执行apt-get命令提示apt-get command not found 先说结论: 在centos下用yum install xxx 不是使用apt-getyum和apt-get的区别: 一般来说著名的linux系统基本上分两大类&#xff1a; RedHat系列&#xf…

.net core 源码解析-mvc route的注册,激活,调用流程(三)

.net core mvc route的注册&#xff0c;激活&#xff0c;调用流程 mvc的入口是route&#xff0c;当前请求的url匹配到合适的route之后&#xff0c;mvc根据route所指定的controller和action激活controller并调用action完成mvc的处理流程。下面我们看看服务器是如何调用route的。…

高可用性的几个级别

转载自 高可用性的几个级别 大家常说高可用&#xff0c;High Availablility&#xff0c;但是一般说到这个词的时候&#xff0c;具体指的什么方案呢&#xff1f; 级别一&#xff1a;FT (Fault Tolerance) 双击热备 通过创建与主实例保持虚拟同步的虚拟机&#xff0c;使应用在服…

使用JDBCTemplate实现与Spring结合,方法公用 ——共用实现类(BaseImpl)

/** * Title: BaseImpl.java * Package org.dao.impl * Description: TODO该方法的主要作用&#xff1a; * author A18ccms A18ccms_gmail_com * date 2017-6-6 下午4:12:02 * version V1.0 */ package org.dao.impl;import java.io.Serializable; import java.lang.refl…

后端讲师管理模块

后端讲师管理模块 后端项目的结构 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hTdcdNmT-1611036676306)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20210118223028941.png)] [外链图片转存失败,源站可能有防盗链机制,…

mysql - Docker Wordpress连接到本地主机上的数据库服务器

视频上面的 docker service create --name mysql -p 3306:3306 --env MYSQL_ROOT_PASSWORDroot \ --env MYSQL_DATABASEwordpress \ --network demo \ --mount typevolume,sourcemysql-data,destination/var/lib/mysql \ mysql:5.7 docker service create -…

CoreCRM 开发实录——开始之新项目的技术选择

2016年11月&#xff0c;接受了一个工作&#xff0c;是对“悟空CRM”进行一些修补。这是一个不错的 CRM&#xff0c;开源&#xff0c;并提供一个 SaaS 的服务。正好微软的 .NET Core 和 ASP.NET Core 也发布了。于是就有了这个想法&#xff1a;使用 ASP.NET Core 来开发一个 CRM…

80%的程序员都不了解的调试技巧

转载自 80%的程序员都不了解的调试技巧 程序员的工作内容&#xff0c;除了大部分时间写代码之外&#xff0c;因为有不少的时间是用在调试代码上。甚至说不是在调试代码&#xff0c;就是即将调试代码。 :) 今天我们来谈谈调试代码的一些技巧&#xff0c;在使用IDE提供的debu…

使用JDBCTemplate实现与Spring结合,方法公用 ——Emp实现类(EmpDaoImpl)

/** * Title: EmpDaoImpl.java * Package org.dao.impl * Description: TODO该方法的主要作用&#xff1a; * author A18ccms A18ccms_gmail_com * date 2017-6-3 下午2:42:51 * version V1.0 */ package org.dao.impl;import java.io.Serializable; import java.util.Li…

复制vmware overLay网络无法ping通 ping www.baidu.com可以

因为忘记关闭防火墙了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 要永久关闭&#xff01;&#xff01; 修改hostname https://blog.csdn.net/qq_27327261/article/details/109100219 关闭防火墙 https://blog.csdn.net/qq_27327261/article/details/1…

2016.NET Core相关内容回顾

每一年的脚步的确是快&#xff0c;转眼间马上就2017。.NET Core 2014年宣布开源以来&#xff0c;在2016年发布了第一个版本&#xff0c;2017年将发布第二个版本&#xff0c;在这新年之际&#xff0c;我们回顾2016年&#xff0c;新的一年&#xff0c;带着理想和抱负继续出发。 1…

使用JDBCTemplate实现与Spring结合,方法公用 ——Spring配置(applicationContext.xml)

<?xml version"1.0" encoding"UTF-8"?> <beansxmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xmlns:p"http://www.springframework.org/schema/p"xs…

微服务化的数据库设计与读写分离

转载自 微服务化的数据库设计与读写分离 数据库永远是应用最关键的一环&#xff0c;同时越到高并发阶段&#xff0c;数据库往往成为瓶颈&#xff0c;如果数据库表和索引不在一开始就进行良好的设计&#xff0c;则后期数据库横向扩展&#xff0c;分库分表都会遇到困难。 对于…