冒泡排序是一种交换排序,它的基本思路是:
两两比较相邻记录的关键字,如果反序则交换,知道没有反序的记录位置。
依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。
最简单排序实现
public void BubbleSort_0(int[] elem) {
for (int i = 1; i < elem.length; i++) {
for (int j = i + 1; j < elem.length; j++) {
if(elem[i] > elem[j]) {
++count;
swap(elem, i, j);
}
}
}
}
先看这段代码,这段代码严格上上并不是标准的冒泡排序算法,因为不满足"两两比较相邻记录"的思想,它的思路就是让每一个关键字都和它后面的每一个关键字比较,如果大则交换,这样第一位置的关键字在一次循环后一定变成最小值。比如待排序的序列是{9, 1, 5, 8, 3, 7, 4, 6, 2},当i=1时,9与1交换后,在第一位置的1与后面的关键字比较都小,因此它为最小值。当i=2时,第二位置先后由9换成5,换成3,换成2,完成第二小的数字交换,依次执行,知道最后。
这段代码简单易懂,但是存在很大的缺陷,当排序好1和2位置后,对其余关键字的排序没帮助(比如数字3反而被换到最后的位置),所以效率非常低
冒泡排序实现
//置换
public void swap(int[] elem, int i, int j) {
int temp = elem[i];
elem[i] = elem[j];
elem[j] = temp;
}
//冒泡
public void BubbleSort(int[] elem) {
for (int i = 1; i < elem.length; i++) {
for (int j = (elem.length - 1); j >= i; j--) {
if(elem[j - 1] > elem[j]) {
++count;
swap(elem, j-1, j);
}
}
}
}
当i=2时,变量j由8反向循环到2,逐个比较,在将关键字2交换到第二位置的同时,也将关键字4和3也有所提升,与第一种相比,在数据量很大的情况下差异会体现出来。
冒泡排序复杂度分析
最好情况下,也就是待排序的表是有序的,会执行n-1次比较,没有数据置换,时间复杂度为O(n);
最坏情况下,即待排序的表是逆序的,需要执行n(n−1)2\frac{n(n-1)}{2}2n(n−1)次,并做等量级的记录移动,时间复杂度为O(n2n^2n2)。
冒泡排序的优化
冒泡排序存在不足和可优化的地方:
- 在排序过程中,执行完最后的排序后,虽然数据已全部排序完备,但程序无法判断是否完成排序,为了解决这一不足,可设置一个标志位flag,将其初始值设置为true,表示被排序的表是一个无序的表,每一次排序开始前设置flag值为false,在进行数据交换时,修改flag为true。在新一轮排序开始时,检查此标志,若此标志为false,表示上一次没有做过交换数据,则结束排序;否则进行排序;
public void BubbleSort_2(int[] elem) {
boolean flag = true;
for (int i = 1; i < elem.length && flag; i++) {
flag = false;
for (int j = (elem.length - 1); j >= i; j--) {
if(elem[j - 1] > elem[j]) {
++count;
swap(elem, j-1, j);
flag = true;
}
}
}
}
- 局部冒泡
在冒泡排序中,一趟扫描有可能无数据交换,也有可能有一次或多次数据交换,在传统的冒泡排序算法及近年来的一些改进的算法中,只记录一趟扫描有无数据交换的信息,对数据交换发生的位置信息则不予处理。为了充分利用这一信息,可以在一趟全局扫描中,对每一反序数据对进行局部冒泡排序处理,称之为局部冒泡排序。局部冒泡排序与冒泡排序算法具有相同的时间复杂度,并且在正序和逆序的情况下,所需的关键字的比较次数和移动次数完全相同。由于局部冒泡排序和冒泡排序的数据移动次数总是相同的,而局部冒泡排序所需关键字的比较次数常少于冒泡排序,这意味着局部冒泡排序很可能在平均比较次数上对冒泡排序有所改进,当比较次数较少的优点不足以抵消其程序复杂度所带来的额外开销,而当数据量较大时,局部冒泡排序的时间性能则明显优于冒泡排序。对于N个无序数据,我们在进行一趟冒泡排序时,如果第k个数据和第k+1个数据逆序,那么对第k+1个数据进行一趟向前的冒泡排序,使其移动到合适的位置,也就是说让前面k+1个数据调节为正序。因为这种冒泡法只对前k+1个数据冒泡处理,所以我们称它为——局部冒泡