上一篇文章「 排序算法 」已经总体的把排序算法的分类和评估方法介绍了一下,今天起我们就开始依次介绍一下各类排序算法的原理和特性。我们就从最容易理解的「 冒泡排序 」开始吧。
1、「 冒泡排序 」是什么?
冒泡排序是一种交换排序,它的思路就是在待排序的数据中,两两比较相邻元素的大小,看是否知足大小顺序的要求,若是知足则不动,若是不知足则让它们互换。而后继续与下一个相邻元素的比较,一直到一次遍历完成。一次遍历的过程就被成为一次冒泡,一次冒泡的结束至少会让一个元素移动到了正确的位置。因此要想让全部元素都排序好,一次冒泡还不行,咱们得重复N次去冒泡,这样最终就完成了N个数据的排序过程。
经过上面的描述,能够看出来冒泡排序在代码实现层面不就是两层循环嘛,哈哈。
下面举例:算法

如图,这是针对数组:5,1,4,2,8 采用冒泡排序进行从小到大的排列,上图中分别进行了三次冒泡后完成了整个排序过程。
先看第一次冒泡:数组
- 从数组的第0位开始,比较5和1,发现5>1,交换位置,交换后数组为:1,5,4,2,8
- 继续下一个元素的比较,比较5和4,发现5>4,交换位置,交换后数组为:1,4,5,2,8
- 继续下一个元素的比较,比较5和2,发现5>2,交换位置,交换后数组为:1,4,2,5,8
- 继续下一个元素的比较,比较5和8,发现5<8,不用交换,数组保持不变:1,4,2,5,8
- 继续下一个元素的比较,发现没有元素了,不用比较了,数组在第一轮冒泡排序后的最终状态就是:1,4,2,5,8 了,此时 元素 8 已经到了正确的位置,其它元素位置仍是不对,须要循环进行下一轮冒泡。
第二次冒泡和第三次冒泡的原理与第一次冒泡同样,这里就不描述了,直接看上图,图中有清晰的流程标注。
咱们在写冒泡排序的时候,有两个事项须要注意:微信
- 冒泡的次数能够减小:
理论上若是数组有N个元素,且这N个元素彻底是倒序的话,咱们须要进行N次冒泡才能够完成排序工做,可是经过上面的示例能够发现,上述数组有5位,可是咱们只进行了三次冒泡就完成了,缘由就是由于数组中有些元素以前就已是有序的了。那咱们怎么判断该用几回冒泡操做呢?
冒泡中止的条件就是:当某次冒泡操做全程都无需进行元素交换,就说明此时这个数组已经达到了彻底有序状态了,无需再进行下一次冒泡了。
上图中的第三次冒泡过程当中,没有一次须要元素交换的,所以就不须要进行第四次冒泡了。
在写代码的时候,须要使用一个变量来作好标记,下面咱们来写一个冒泡代码:
算法题:对数组arr进行从小到大的排序,假设数组arr不为空,arr的长度为n
思路:有两种方式均可以,一个是从数组前日后冒泡,将最大的元素移动到最后面,另外一种方式是从数组的后面往前冒泡,将最小的元素移动到最前面。
public class BubbleSort {
/**
* 从前日后冒泡
* 上面的图片就是采用这种方式
*/
public void bubbleSort1(int[] arr, int n) { for (int i = 0; i < n; i++) {
// flag是用来标记本次冒泡中是否有元素交换,用来决定冒泡中止条件的
boolean flag = false;
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;
flag = true; // 若是有元素交换了,就设置为true
}
}
// 一次冒泡下来没有元素交换,就提早退出
if (!flag) break;
}
}
/**
* 从后往前冒泡
*/
public void bubbleSort2(int[] arr, int n) { for (int i = 0; i < n; i++) {
// flag是用来标记本次冒泡中是否有元素交换,用来决定冒泡中止条件的
boolean flag = false;
for (int j = n-i-1; j > i; j--) {
// 从第最后一个开始,相邻元素两两比较,若是前一个比后一个大则交换
if (arr[j-1] > arr[j]) {
int temp = arr[j-1];
arr[j-1] = arr[j];
arr[j] = temp;
flag = true; // 若是有元素交换了,就设置为true
}
}
// 一次冒泡下来没有元素交换,就提早退出
if (!flag) break;
}
}
}
- 冒泡必定是对比相邻元素:
冒泡排序的原则很简单,就是相邻的两两对比而后判断是否交换。但其中有个新人很容易疏忽的就是“相邻”这个词,咱们在循环中对比的元素必定是要相邻的,不能拿着某个元素依次对比数组中的全部元素(好比先拿数组0位元素依次对比其它元素,将最小的置换到第0位,而后再拿数组1位元素依次对比剩下全部元素,将剩下元素最小的置换到第1位,依次循环),虽然这种方式也能最后排序也能完成,可是效率很是的低。为何这种方式效率低呢?
由于这种方式每一次元素交换,虽然都将当前最小的元素移动到了正确的位置,可是对于其它元素的位置没有半点改进,甚至会因为交换致使其它比较小的元素此次遍历中移动到后面。
而采用“相邻元素两两对比”的方式,每次冒泡不只能将一个元素移动到正确的位置,还能附带着对其它元素的位置有改进。
2、「 冒泡排序 」的性能怎么样?
咱们按照前一篇文章讲到的排序算法评估方法来对「 冒泡排序 」进行一下评估:数据结构
- 时间复杂度:
冒泡排序原理就是在两层循环里进行两两对比嘛,因此简单去思考的话,通常状况下的时间复杂度就是O(n*n)了。可是实际仍是得看数据状况,若是待排序的数据自己就是有序的,其实咱们只须要作依次冒泡就完成了(也就是一次循环),那么此时就是最好时间复杂度:O(n),若是待排序的数据所有都是逆序的,那咱们须要作 n(n-1)/2 次循环,最坏时间复杂度就是:O(n*n)了。
- 空间复杂度:
经过咱们对冒泡排序原理的了解,知道冒泡排序在排序的过程当中,不须要占用不少额外的空间(就是在交换元素的时候须要临时变量存一存,这里须要的额外空间开销是常量级的),所以冒泡排序的空间复杂度为O(1)了。
- 排序稳定性:
上一篇介绍过了排序算法稳定性的定义,这里不重复介绍了。对于冒泡排序而言,在作元素对比的时候,若是大小顺序不知足要求,则将它们进行交换,若是知足要求,或者元素相等,则啥都不作。可知,在元素至关的状况下,位置没有发生变化,所以它是排序稳定的。
- 算法复杂性:
冒泡排序的算法不管是其设计思路上,仍是代码的编写上都不复杂,所以冒泡排序算法复杂性是比较简单的。
以上,就是对数据结构中「 冒泡排序 」的一些思考,您有什么疑问吗?
码字不易啊,喜欢的话不妨转发朋友,或点击文章右下角的“在看”吧。😊
本文原创发布于微信公众号「 不止思考 」,欢迎关注。涉及 思惟认知、我的成长、架构、大数据、Web技术 等。架构