Java基础算法(4)——冒泡排序
1.冒泡排序思想与原理简述
冒泡排序是一种最简单的交换排序,通过相邻元素两两比较,如果逆序(若需排为升序,则逆序是前者大于后者),则进行交换,使得大的元素像气泡一样逐渐往上(升序即大元素右移)。
每次遍历都把最大值排到了未排序序列的最后
需要遍历(排序)的次数:数组长度-1 (最后就剩一个元素了,不需要再排序了)
(简单说:第n次排序,都是把第n大的数,排在倒数第n位)
2.完整代码实现(含注释)
简洁版
//简洁版
int N = a.length;
for (int i=0;i<N-1;i++){
for (int j=0;j<N-1-i;j++){
if (!less(a[j],a[j+1])) {
exch(a,j,j+1);
}
}
}
详细版(含注释,运行样例)
与上一篇文章选择排序类似,继承排序类模板类Template
package Algorithm.Sort;
/**
* 排序(4)
* 冒泡排序
* 以升序为例:前面的数大于后面的数则交换这两个数的位置
* 排序次数 数组长度-1
* 每次遍历都把最大值排到了未排序序列的最后
* (简单说:第n次排序,都是把第n大的数,排在倒数第n位)
*/
public class BubbleSort extends Template{
//升序
public static void sort(Comparable[] a) {
int N = a.length;//数组长度
for (int i=0;i<N-1;i++){
for (int j=0;j<N-1-i;j++){
if (!less(a[j],a[j+1])) {//a[j]>a[j+1]则交换
exch(a,j,j+1);
}
}
System.out.println("第"+(i+1)+"次遍历结果,把第"+(i+1)+"大的数,排在倒数第"+(i+1)+"位");
show(a);
}
}
public static void main(String[] args) {
String[] a = {"c", "s", "d", "n", "x", "i", "n", "g", "w", "e", "i","s","h","e","l","l","s","o","r","t"};
System.out.println("冒泡排序前");
show(a);
sort(a);
assert isSorted(a);
System.out.println("冒泡排序后");
show(a);
}
}
3.运行效果
冒泡排序前
c s d n x i n g w e i s h e l l s o r t
冒泡排序后
c d e e g h i i l l n n o r s s s t w x
详细过程(每次遍历,都打印一遍结果)
冒泡排序前
c s d n x i n g w e i s h e l l s o r t
第1次遍历结果,把第1大的数,排在倒数第1位
c d n s i n g w e i s h e l l s o r t x
第2次遍历结果,把第2大的数,排在倒数第2位
c d n i n g s e i s h e l l s o r t w x
第3次遍历结果,把第3大的数,排在倒数第3位
c d i n g n e i s h e l l s o r s t w x
第4次遍历结果,把第4大的数,排在倒数第4位
c d i g n e i n h e l l s o r s s t w x
第5次遍历结果,把第5大的数,排在倒数第5位
c d g i e i n h e l l n o r s s s t w x
第6次遍历结果,把第6大的数,排在倒数第6位
c d g e i i h e l l n n o r s s s t w x
第7次遍历结果,把第7大的数,排在倒数第7位
c d e g i h e i l l n n o r s s s t w x
第8次遍历结果,把第8大的数,排在倒数第8位
c d e g h e i i l l n n o r s s s t w x
第9次遍历结果,把第9大的数,排在倒数第9位
c d e g e h i i l l n n o r s s s t w x
第10次遍历结果,把第10大的数,排在倒数第10位
c d e e g h i i l l n n o r s s s t w x
第11次遍历结果,把第11大的数,排在倒数第11位
c d e e g h i i l l n n o r s s s t w x
第12次遍历结果,把第12大的数,排在倒数第12位
c d e e g h i i l l n n o r s s s t w x
第13次遍历结果,把第13大的数,排在倒数第13位
c d e e g h i i l l n n o r s s s t w x
第14次遍历结果,把第14大的数,排在倒数第14位
c d e e g h i i l l n n o r s s s t w x
第15次遍历结果,把第15大的数,排在倒数第15位
c d e e g h i i l l n n o r s s s t w x
第16次遍历结果,把第16大的数,排在倒数第16位
c d e e g h i i l l n n o r s s s t w x
第17次遍历结果,把第17大的数,排在倒数第17位
c d e e g h i i l l n n o r s s s t w x
第18次遍历结果,把第18大的数,排在倒数第18位
c d e e g h i i l l n n o r s s s t w x
第19次遍历结果,把第19大的数,排在倒数第19位
c d e e g h i i l l n n o r s s s t w x
冒泡排序后
c d e e g h i i l l n n o r s s s t w x
4.冒泡排序简单优化
优化缘由:
观察上述冒泡排序详细过程可知,在第十次其实就已经排好了(第十次结果字体加粗了,便于查看)。
因此,后面几次的遍历(排序)其实并没有交换元素位置。
优化方案:
设立一个标识变量,用于鉴别本次遍历(排序)中是否有交换过元素位置。
标识变量常用Boolean类型的true/false,或者用int类型0/1表示(当然其他的也行)。
假设标识变量定义如下:
boolean flag=false;
//设置标识变量,标识本次遍历中是否有元素交换
// (flag默认为true,代表为没有交换过)
当本次遍历没有发生元素交换时,即标识变量为false,则跳出循环,结束遍历,以为此时数组已经有序。
具体代码如下(含注释):
//升序(简单优化后)
public static void sort(Comparable[] a) {
int N = a.length;//数组长度
boolean flag=false;//设置标识变量,标识本次遍历中是否有元素交换
// (flag默认为true,代表为没有交换过)
for (int i = 0; i < N-1; i++) {
for (int j = 0; j < N - 1 - i; j++) {
if (!less(a[j], a[j + 1])) {//a[j]>a[j+1]则交换
exch(a, j, j + 1);
flag=true;//交换过元素
}
}
System.out.println("第" + (i + 1) + "次遍历结果,把第" + (i + 1) + "大的数,排在倒数第" + (i + 1) + "位");
show(a);
if(!flag){//若本次遍历中没有发生元素交换,则数组已经排序好,退出循环
break;
}else {
flag=false;
}
}
}
优化后的运行效果:
冒泡排序前
c s d n x i n g w e i s h e l l s o r t
第1次遍历结果,把第1大的数,排在倒数第1位
c d n s i n g w e i s h e l l s o r t x
第2次遍历结果,把第2大的数,排在倒数第2位
c d n i n g s e i s h e l l s o r t w x
第3次遍历结果,把第3大的数,排在倒数第3位
c d i n g n e i s h e l l s o r s t w x
第4次遍历结果,把第4大的数,排在倒数第4位
c d i g n e i n h e l l s o r s s t w x
第5次遍历结果,把第5大的数,排在倒数第5位
c d g i e i n h e l l n o r s s s t w x
第6次遍历结果,把第6大的数,排在倒数第6位
c d g e i i h e l l n n o r s s s t w x
第7次遍历结果,把第7大的数,排在倒数第7位
c d e g i h e i l l n n o r s s s t w x
第8次遍历结果,把第8大的数,排在倒数第8位
c d e g h e i i l l n n o r s s s t w x
第9次遍历结果,把第9大的数,排在倒数第9位
c d e g e h i i l l n n o r s s s t w x
第10次遍历结果,把第10大的数,排在倒数第10位
c d e e g h i i l l n n o r s s s t w x
第11次遍历结果,把第11大的数,排在倒数第11位
c d e e g h i i l l n n o r s s s t w x
冒泡排序后
c d e e g h i i l l n n o r s s s t w x
可以看到遍历了11次,而优化前遍历了19次。
在这个例子中为什么是遍历11次呢,因为第10次遍历完后已经排序好了,因此第11次遍历中没有发生交换,标识变量为false,会退出遍历的循环。