冒泡排序的基本思想是:每次比较两个相邻的元素,如果它们的顺序错误就把它们交换过来。
例如我们需要将12 35 99 18 76 这5 个数进行从大到小的排序。既然是从大到小排序,也就是说越小的越靠后。
首先比较第1 位和第2 位的大小,现在第1 位是12,第2 位是35。发现12 比35 要小,因为我们希望越小越靠后嘛,因此需要交换这两个数的位置。交换之后这5 个数的顺序是35 12 99 18 76。
按照刚才的方法,继续比较第2 位和第3 位的大小,第2 位是12,第3 位是99。12 比99 要小,因此需要交换这两个数的位置。交换之后这5 个数的顺序是35 99 12 18 76。
根据刚才的规则,继续比较第3 位和第4 位的大小,如果第3 位比第4 位小,则交换位置。交换之后这5 个数的顺序是35 99 18 12 76。
最后,比较第4 位和第5 位。4 次比较之后5 个数的顺序是35 99 18 76 12。经过4 次比较后我们发现最小的一个数已经就位(已经在最后一位,请注意12 这个数的移动过程),是不是很神奇。现在再来回忆一下刚才比较的过程。每次都是比较相邻的两个数,如果后面的数比前面的数大,则交换这两个数的位置。一直比较下去直到最后两个数比较完毕后,最小的数就在最后一个了。就如同是一个气泡,一步一步往后“翻滚”,直到最后一位。所以这个排序的方法有一个很好听的名字“冒泡排序”。
说到这里其实我们的排序只将5 个数中最小的一个归位了。每将一个数归位我们将其称为“一趟”。下面我们将继续重复刚才的过程,将剩下的4 个数一一归位。
好,现在开始“第二趟”,目标是将第2 小的数归位。首先还是先比较第1 位和第2 位,如果第1 位比第2 位小,则交换位置。交换之后这5 个数的顺序是99 35 18 76 12。接下来你应该都会了,依次比较第2 位和第3 位,第3 位和第4 位。注意此时已经不需要再比较第4位和第5 位。因为在第一趟结束后已经可以确定第5 位上放的是最小的了。第二趟结束之后这5 个数的顺序是99 35 76 18 12。
“第三趟”也是一样的。第三趟之后这5 个数的顺序是99 76 35 18 12。现在到了最后一趟“第四趟”。有的同学又要问了,这不是已经排好了吗?还要继续?当然,这里纯属巧合,你若用别的数试一试可能就不是了。你能找出这样的数据样例来吗?
请试一试。
“冒泡排序”的原理是:每一趟只能确定将一个数归位。即第一趟只能确定将末位上的数(即第5 位)归位,第二趟只能将倒数第2 位上的数(即第4 位)归位,第三趟只能将倒数第3 位上的数(即第3 位)归位,而现在前面还有两个位置上的数没有归位,因此我们仍然需要进行“第四趟”。“第四趟”只需要比较第1 位和第2 位的大小。因为后面三个位置上的数归位了,现在第1 位是99,第2 位是76,无需交换。这5 个数的顺序不变仍然是99 76 35 18 12。到此排 序完美结束了,5 个数已经有4 个数归位,那最后一个数也只能放在第1 位了。 最后我们总结一下:如果有n 个数进行排序,只需将n1 个数归位,也就是说要进行n-1 趟操作。而“每一趟”都需要从第1 位开始进行相邻两个数的比较,将较小的一个数放在后面,比较完毕后向后挪一位继续比较下面两个相邻数的大小,重复此步骤,直到最后一个尚未归位的数,已经归位的数则无需再进行比较(已经归位的数你还比较个啥,浪费表情)。这个算法是不是很强悍?记得我每次拍集体照的时候就总是被别人换来换去的,当时特别烦。不知道发明此算法的人当时的灵感是否来源于此。啰里吧嗦地说了这么多,下面是代码。建议先自己尝试去实现一下看看,再来看我是如何实现的。
```
public class BubbleSort{
public static void main(String[] args){
int score[] = {67, 69, 75, 87, 89, 90, 99, 100};
for (int i = 0; i < score.length -1; i++){ //最多做n-1趟排序
for(int j = 0 ;j < score.length - i - 1; j++){ //对当前无序区间score[0......length-i-1]进行排序(j的范围很关键,这个范围是在逐步缩小的)
if(score[j] < score[j + 1]){ //把小的值交换到后面
int temp = score[j];
score[j] = score[j + 1];
score[j + 1] = temp;
}
}
System.out.print("第" + (i + 1) + "次排序结果:");
for(int a = 0; a < score.length; a++){
System.out.print(score[a] + "\t");
}
System.out.println("");
}
System.out.print("最终排序结果:");
for(int a = 0; a < score.length; a++){
System.out.print(score[a] + "\t");
}
}
}
```
冒泡排序的核心部分是双重嵌套循环。不难看出冒泡排序的时间复杂度是O(N2)。这是一个非常高的时间复杂度。冒泡排序早在1956 年就有人开始研究,之后有很多人都尝试过对冒泡排序进行改进,但结果却令人失望。如Donald E. Knuth(中文名为高德纳,1974 年图灵奖获得者)所说:“冒泡排序除了它迷人的名字和导致了某些有趣的理论问题这一事实之外,似乎没有什么值得推荐的。”你可能要问:那还有没有更好的排序算法呢?不要走开,请看下节——快速排序。