冒泡排序
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
可以看出对于随机数组,常用的冒泡性能最好,接下来是简单选择排序,标准冒泡和改进的冒泡效率不如初学常用的冒泡高。改进的冒泡排序适合于接近有序或已经有序的情况。