在介绍冒泡排序之前,先介绍一下交换排序,如果 i<j 而 a[i]>a[j],即大的数排在小的数前面,称为 a[i] 和 a[j] 构成 逆序 。在排序的过程中,通过比较元素的大小 发现逆序就交换它们的位置,这样的排序方法称为:交换排序 。交换可以 消除 逆序。当所有的逆序都消除后,排序就完成了。或者说,没有逆序的序列就是有序序列 。其中 冒泡排序 和 快速排序 是典型的交换排序方法。
冒泡排序
冒泡排序也叫 气泡排序。基本的原理就是通过 反复的扫描数组,从数组 末尾开始比较两个 相邻元素a[i] 与a[i+1] 的大小,若 a[i]>a[i+1],就交换这两个值。并不断让 i- -(相当于让比较的两个相邻元素不断往前面移动,这样的话最终数组里面的 最小值就会交换到数组的 最前面),每比完一个循环,就是最小值已经送到了最前面,那么前面的最小值在接下来的循环就不会再去比较了(相对于 每一轮循环都将后面无序数组的最小值送入前面来)下面的图模拟了这样比较过程。
冒泡排序其实有 两种扫描方法,一种是上面图中描述的那样, 自下而上地扫描,称为 上升法可使 最小值上升到数组的开头。(就是从小到大排序)另一种便是 自上而下地扫描,称为 下降法,一遍扫描可使 最大值元素排到数组最末尾。(就是从大到小排序)。因为扫描过程中,总是习惯让大的元素排到数组后面(下降),小元素排数组前面(上升),酷似水中冒出的气泡,冒泡排序因此得名。下面来演示一下 使用Java语言来编写的简单冒泡排序算法。
在这里插入代码片import java.util.Arrays;import java.util.Arrays;
public class MaoPao_JianDanPaiXu {
public static void main(String[] args) {
// 打印一下没排序之前的数组值顺序
int[] a = new int[] {4,6,3,9,8,7};
System.out.print("排序之前的元素顺序:");
// 使用 PaiXun() 方法排序一下后再打印 PaiXun(a);
System.out.println(Arrays.toString(a));
System.out.print("排序之后的元素顺序:");
System.out.println(Arrays.toString(a));
}
public static void PaiXun(int[] a) {
int k,n=0; // 临时中间变量
for(int i=0;i<a.length-1;i++) {
for(int j=a.length-2;j>=i;j--) {
if(a[j]>a[j+1]) {
// 调换如果“逆序”调换两个值的顺序
k = a[j];
a[j]=a[j+1];
a[j+1]=k;
}
}
n++;
}
System.out.println("本次扫描了:"+n+"次");
}
}
输出结果:排序之前的元素顺序:[4, 6, 3, 9, 8, 7] 本次扫描了:5次排序之后的元素顺序:[3, 4, 6, 7, 8, 9] 当然如果想改成从大到小排序只要把if语句里面的>号改成<号就可以了,不过这种排序算法的 效率是 非常低的。因为不管原始的数组里面的数据排列情况如何,算法的外循环都执行了a.length-1次,这种扫描方式是一种全局扫描方式。实际上,如果在某一遍的扫描中 没有发现逆序(也就是没有元素交互位置),那不就说明全部元素已经排序好了,没有必要再接着循环了,可以结束排序了。根据这一思想,我们可以使用一个 交互吗 标记 flag,来 记录本次扫描是否发生过元素交换。根据 flag 的值是 true 还是 false 决定是否还要进行下一次的循环。下面给出了一个示例图。
根据以上改进的算法如下所示:
在这里插入代码片import java.util.Arrays;import java.util.Arrays;
public class MaoPao_JianDanPaiXu {
public static void main(String[] args) {
int[] a = new int[] {4,6,3,9,8,7};
// 打印一下没排序之前的数组值顺序
System.out.print("排序之前的元素顺序:");
System.out.println(Arrays.toString(a));
// 使用 PaiXun() 方法排序一下后再打印
PaiXun(a);
System.out.print("排序之后的元素顺序:");
System.out.println(Arrays.toString(a));
}
public static void PaiXun(int[] a) {
int i,k,n=0; // n是记录外循环的次数
i=a.length-2;
boolean flag = true;
while(flag) {
flag = false; // 每次进入循环把flag的值设为假
for(int j=0;j<=i;j++) {
// 该次扫描调换了元素,又把flag设为真
if(a[j]>a[j+1]) {
k = a[j];
a[j] = a[j+1];
a[j+1] = k;
flag = true;
}
i--;
}
n++;
}
System.out.println("本次排序扫描了:"+n+"次");
}
}
输出结果:排序之前的元素顺序:[4, 6, 3, 9, 8, 7] 本次排序扫描了:3次排序之后的元素顺序:[3, 4, 6, 9, 8, 7] 不出所料,本来要扫描5遍的变成了3遍。效率比之前的好了些。如果是在数据量很大的情况下还是蛮有改善的。( 注意!扫描的次数不是排序总循环的次数 )