在上一篇:数据结构与算法系列十(排序概述) 中,我们列举了常用的排序算法,以及分析了如何综合衡量排序算法的优劣。如果你还没有看上一篇的内容,可以去看一看,应该会有所收获。
从这一篇开始,我们把每一种排序算法,从算法的思想,到代码实现都做一个分享。那么你准备好了吗?
我们这一篇的主角是:冒泡排序
#考考你:
1.你知道冒泡排序的核心思想吗?
2.你能用java实现冒泡排序吗?
3.你能写出更优秀的冒泡排序代码吗?
案例
冒泡排序思想
假设有一个待排序序列:[4, 5, 6, 3, 2, 1]。我们需要按照升序进行排序,排序后的序列是这样的:[1, 2, 3, 4, 5, 6]。
如何通过冒泡排序实现呢?
这里我们先来理解冒泡排序中的冒泡两个字。所谓冒泡就像平静的水面,鱼儿从水底吹气一样,一个一个的水泡向上冒,很诗情画意,我们都向往这样的生活环境对吧。
那么请保持这个美好的姿势,我们一起来理解冒泡排序的思想,先看一个图:
冒泡排序核心思想:
假设待排序序列有n个元素,需要经过n次冒泡,每一次冒泡过程中依次比较交换相邻的两个元素,一次冒泡结束,都会有1个元素到达指定的目标位置。这里的关键词有:
1.n个元素,n次冒泡
2.比较交换相邻元素
冒泡排序代码实现
排序代码
/**
* 冒泡排序:普通实现版本
* @param array:待排序数组
* @param n:待排序数组大小
*/
public static void sort_1(Integer [] array,int n){
// 如果排序数组规模小于等于1,直接返回
if(n <= 1){
return;
}
// 有n个元素,进行n次冒泡
for(int i = 0; i < n; i++){
// 每一次冒泡,比较交换相邻两个元素
for(int j = 0; j < n-i-1; j++){
if(array[j] > array[j+1]){
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
}
测试代码
public static void main(String[] args) {
// 初始化测试数组
Integer[] array = {4,5,6,3,2,1};
// 排序前
System.out.println("1.排序前数组:" + Arrays.deepToString(array));
// 排序后
sort_1(array,array.length);
// 排序后
System.out.println("2.排序后数组:" + Arrays.deepToString(array));
}
测试结果:
D:\02teach\01soft\jdk8\bin\java
com.anan.algorithm.sort.BubbleSort
1.排序前数组:[4, 5, 6, 3, 2, 1]
2.排序后数组:[1, 2, 3, 4, 5, 6]
Process finished with exit code 0
冒泡排序实现优化
优化分析
在冒泡排序普通实现版本,我们严格按照冒泡排序的思想:n个元素、n次冒泡,每一次冒泡依次比较交换相邻元素。实现了一个冒泡排序。
在这里,请你先简单思考一下:有没有更优化的实现方式呢?
我们先来分析一下冒泡排序算法的时间复杂度,结合代码我们发现冒泡排序的时间复杂度是:O(n^2) ,有两次for循环,这不是一个高效的算法对吧。如果说我们能够减少冒泡的次数,则可以极大提升算法的执行效率。
问题来了:什么情况下可以减少冒泡次数呢?
其实我们只要结合冒泡排序算法的核心思想后半部分:比较交换相邻的元素。如果说在一次冒泡中,没有发生相邻元素的交换,那说明待排序序列已经有序了,不管后面还剩下多少次冒泡,我们都不需要再进行冒泡下去了。这样是不是就减少冒泡的次数了呢
关于减少冒泡次数的分析,如果你暂时没有理解过来的话,没有关系。请看我们下面的代码实现,相信结合代码你会恍然大悟。
优化代码实现
/**
* 冒泡排序:优化实现版本
* @param array:待排序数组
* @param n:待排序数组大小
*/
public static void sort_2(Integer [] array,int n){
// 如果排序数组规模小于等于1,直接返回
if(n <= 1){
return;
}
// 优化标识
// 如果某一次冒泡过程中,没有发生数据交换
// 则说明已经排好了序,不需要在继续冒泡
boolean flag = false;
// n个元素,n次冒泡
for(int i = 0; i < n; i++){
// 重置是否发生交换标识
flag = false;
// 每一次冒泡中,比较交换相邻元素
for(int j = 0; j < n-i-1; j++){
if(array[j] > array[j+1]){
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
// 发生了数据交换
flag = true;
}
}
// 一次冒泡结束,检查是否发生了数据交换
// 如果没有发生数据交换,说明序列已经有序,不需要再继续冒泡了
System.out.println("第【" + (i+1) + "】次冒泡.");
if( !flag){
break;
}
}
}
测试代码
public static void main(String[] args) {
// 初始化测试数组
Integer[] array = {4,5,6,3,2,1};
// 排序前
System.out.println("1.排序前数组:" + Arrays.deepToString(array));
// 第一次排序
System.out.println("2.第一次排序-------------------------------start");
sort_2(array,array.length);
System.out.println("3.第一次排序后数组:" + Arrays.deepToString(array));
// 第二次排序
System.out.println("4.第二次排序-------------------------------start");
sort_2(array,array.length);
System.out.println("5.第二次排序后数组:" + Arrays.deepToString(array));
}
讨论分享
#考考你答案:
1.你知道冒泡排序的核心思想吗?
1.1.假设待排序序列有n个元素
1.2.整个排序过程中,需要n次冒泡
1.3.每一次冒泡过程中,依次比较交换相邻两个元素
1.4.一次冒泡结束,都会有一个元素到达指定的位置
2.你能用java实现冒泡排序吗?
2.1.参考【3.2】节案例实现
3.你能写出更优秀的冒泡排序代码吗?
3.1.结合冒泡排序算法的核心思想:n个元素、n次冒泡,每一次冒泡依次比较交换相邻的两个元素
3.2.如果在某一次冒泡中,没有发生元素交换
3.3.说明待排序序列已经有序,不需要再进行冒泡下去