1. 冒泡与选择排序及其比较
冒泡排序
1. 思想
冒泡排序(Bubble Sort)是一种交换排序,基本思路是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
2. 实现
2.1 初学常用的一种
public static <T extends Comparable<? super T>> void BubbleSort(T[] a) {
int length = a.length;
for (int i = 0; i < length; i++) {
for (int j = i+1; j < length; j++) {
if (a[i].compareTo(a[j]) > 0) {
Object obj = a[i];
a[j] = a[i];
a[i] = (T)obj;
} // end if
} // end for
} // end for
} // end BubbleSort
缺陷:每一次内循环结束时,对其余的关键字没有帮助,甚至把原来靠近正确排序位置的记录交换到较远的地方。即,算法是低效的。
2.2 正宗的冒泡排序
public static <T extends Comparable<? super T>> void BubbleSort(T[] a) {
int length = a.length;
for (int i = 0; i < length - 1; i++) {
for (int j = length - 2; j >= i; j--) {
if (a[j].compareTo(a[j+1]) > 0) {
Object obj = a[i];
a[i] = a[j];
a[j] = (T)obj;
} // end if
} // end for
} // end for
} // end BubbleSort
显然这一算法比之前的实现要有效,图中较小的数字如同气泡慢慢浮到上面,因此将此算法命名为冒泡排序。
2.3 冒泡排序的优化
对于已经有序或接近有序的集合时,会进行很多次不必要的循环比较,为此,需要改进实现,设置一个flag记录在一次循环比较中是否有交换操作,如果没有说明集合已经有序。
public static <T extends Comparable<? super T>> void BubbleSort(T[] a) {
int length = a.length;
boolean flag = true; // 用flag作为标记
for (int i = 0; (i < length - 1) && flag; i++) {
flag = false;
for (int j = length - 2; j >= i; j--) {
if (a[j].compareTo(a[j+1]) > 0) {
Object obj = a[i];
a[i] = a[j];
a[j] = (T)obj;
flag = true; // 有数据交换则为true
} // end if
} // end for
} // end for
} // end BubbleSort
2.4 冒泡排序复杂度分析
最好的情况下,也就是数组有序时,根据最后改进的代码,需要比较n-1次关键字,没有数据交换,时间复杂度为O(n)。最坏的情况下,即待排序记录全为倒序,此时比较1+2+3+4+…+(n-1) = n(n-1)/2次,并作等数量级的记录移动。所以时间复杂度为O(n2)。
简单选择排序
1. 思想
冒泡排序的思想就不断地在交换,通过交换完成最终的排序。这种方式太繁琐,可不可以在确定位置的时候在交换,减少交换操作,完成只交换一次就完成相应关键字的排序定位?这就是选择排序的初步思想。
2. 排序算法
简单选择排序(Simple Selection Sort)就是通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1 ≤ i ≤ n)个记录交换。
// simple selection sort
public static <T extends Comparable<? super T>> void SelectSort(T[] a) {
int length = a.length;
for (int i = 0; i < length - 1; i++) {
int min = i;
for (int j = i+1; j < length; j++) {
if (a[min].compareTo(a[j]) > 0) {
min = j;
} // end if
} // end for
if (i != min) {
Object obj = a[min];
a[min] = a[i];
a[i] = (T)obj;
} // end if
} // end for
} // end SelectSort
3. 简单选择排序复杂度分析
从简单选择排序过程看,最大的特点是减少了移动数据的次数,这样节约了时间。无论最好还是最差的情况下,比较次数都是一样的,第i趟要比较n-i次关键字,共需要比较(n-1)+(n-2)+…+2+1=n(n-1)/2次,最好情况下,即有序时,交换0次,最坏情况下,即逆序时,交换n-1次。最终排序时间为比较和移动的总和,时间复杂度为O(n2)。
尽管与冒泡排序同为O(n2),但简单选择排序的性能还是要略优于冒泡排序。(下列比较却不是!)
冒泡与选择排序对比
import java.util.Arrays;
/**
* sort for Array
* @author Administrator
*/
public class Sort {
// 非标准的冒泡排序,最简单的交换排序!(让每一个关键字,都和它后面的每一个关键字比较,如果大则交换)
public static void BubbleSort1(int[] a) {
int length = a.length;
for (int i = 0; i < length; i++) {
for (int j = i+1; j < length; j++) {
if (a[i] > a[j]) {
int obj = a[i];
a[i] = a[j];
a[j] = obj;
} // end if
} // end for
} // end for
} // end BubbleSort
// 标准冒泡排序
public static void BubbleSort2(int[] a) {
int length = a.length;
for (int i = 0; i < length - 1; i++) {
for (int j = length - 2; j >= i; j--) {
if (a[j] > a[j+1]) {
int obj = a[j];
a[j] = a[j+1];
a[j+1] = obj;
} // end if
} // end for
} // end for
} // end BubbleSort
public static void BubbleSort3(int[] a) {
int length = a.length;
boolean flag = true; // 用flag作为标记
for (int i = 0; (i < length - 1) && flag; i++) {
flag = false;
for (int j = length - 2; j >= i; j--) {
if (a[j] > a[j+1]) {
int obj = a[j];
a[j] = a[j+1];
a[j+1] = obj;
flag = true; // 有数据交换则为true
} // end if
} // end for
} // end for
} // end BubbleSort
// simple selection sort
public static void SelectSort(int[] a) {
int length = a.length;
for (int i = 0; i < length - 1; i++) {
int min = i;
for (int j = i+1; j < length; j++) {
if (a[min] > a[j]) {
min = j;
} // end if
} // end for
if (i != min) {
int obj = a[min];
a[min] = a[i];
a[i] = obj;
} // end if
} // end for
} // end SelectSort
public static void main(String[] args) {
// 随机生成50000、50_0000的整数
int[] a = new int[50_0000];
for (int i = 0; i < a.length; i++) {
a[i] = (int)(Math.random() * 500);
//System.out.print(a[i] + " ");
}
// System.out.println();
// 保证各个排序算法使用的数据一样
int[] a2 = Arrays.copyOf(a, a.length);
int[] a3 = Arrays.copyOf(a, a.length);
int[] a4 = Arrays.copyOf(a, a.length);
Date d1 = new Date();
BubbleSort1(a); // 最常用的初学实现 50000:919,962,1032 500000:59425,60701,59811
System.out.println(new Date().getTime() - d1.getTime());
Date d2 = new Date();
BubbleSort2(a2); // 标准冒泡 50000:5332,5300,5957 500000:491104,480838,478621
System.out.println(new Date().getTime() - d2.getTime());
Date d3 = new Date();
BubbleSort3(a3); // 改进冒泡 50000: 5477,5648,5696 500000:526451,522458,503981
System.out.println(new Date().getTime() - d3.getTime());
Date d4 = new Date();
SelectSort(a4); // 50000: 1118,1256,1076 500000:107144,95680,94796
System.out.println(new Date().getTime() - d4.getTime());
}
} // end Sort
可以看出对于随机数组,常用的冒泡性能最好,接下来是简单选择排序,标准冒泡和改进的冒泡效率不如初学常用的冒泡高。改进的冒泡排序适合于接近有序或已经有序的情况。