關於排序的算法,大約分爲兩大類:順序排序與對數排序。
順序排序一般使用一對嵌套的循環結構(多爲兩個for循環),因此排序n個元素需要大約n的2次方的比較。比較常用的順序排序有(1)選擇排序法 (2)插入排序法 (3)冒泡排序法
對數排序一般需要大約n*log2n(2爲底數)次比較。這兩種排序,當n越大的時候,他們性能上的差別就越大。快速排序與歸併排序都屬於對數排序,同樣是使用遞歸。
1)選擇排序法
- public class Sort {
- // 選擇排序
- public static void selectionSort(Comparable[] data) {
- int min;
- Comparable temp;
- // 從第一個元素開始比較,找出最小那個,放在第一位
- // 從第二個元素開始比較,找出最小那個,放在第二位
- // 總共需要放N-1次
- for (int index = 0; index < data.length - 1; index++) {
- min = index;
- for (int scan = index + 1; scan < data.length; scan++) {
- if (data[scan].compareTo(data[min]) < 0) {
- min = scan;
- }
- }
- //跳回到第一層循環的時候,才進行交換,把最小的數放在第一位...如此類推
- temp = data[min];
- data[min] = data[index];
- data[index] = temp;
- }
- }
- }
策略是:搜索整個數組,找到最小值。然後將這個值與第一個位置上的值交換。然後搜索除第一個值錢外的次小值,然後與第二個位置上的值進行交換。如此類推。這種算法最簡單易明
2)插入排序
- // 插入排序
- public static void insertionSort(Comparable[] data) {
- // 插入一個數,與前面的數比較,放到適當的位置,如此類推
- for (int index = 1; index < data.length; index++) {
- Comparable temp = data[index];
- int position = index;
- while (position > 0 && data[position - 1].compareTo(temp) > 0) {
- data[position] = data[position - 1];
- position--;
- }
- data[position] = temp;
- }
- }
插入排序相對於選擇排序要複雜一些,策略是:每插入一個數,與前面的所有數進行比較, 然後把這個數放到適當的位置。比如:數組裏只有3,插入6,3與6排序後,插入4,然後4就放到第2位,之後插入5,3與4位置不變,5放在第3位,6換 到第4位...插入的過程需要移動數組的其他值,爲插入的元素騰出存儲空間。
3)冒泡算法
- public static void bubbleSort2(Comparable[] data) {
- Comparable temp;
- for (int i = 0; i < data.length; i++) {
- for (int j = 0; j < data.length - 1 - i; j++) {
- if (data[j].compareTo(data[j + 1]) > 0) {
- temp = data[j + 1];
- data[j + 1] = data[j];
- data[j] = temp;
- }
- }
- }
- }
冒泡算法大家應該最熟悉了...它的策略是:第一次對數組所有值進行比較,重複地比較相鄰兩個元素,比較後進行交換,把最大值移動到最後一個位置,然後再掃描除最後一位外的數組其他值,不斷進行交換,把次大值移到倒數第2位,如此類推。每次比較都找出該次所有數中的最大值
上面3種順序排序實現都很相似,效率也接近
現在看看如何使用遞歸實現排序
4)快速排序
- public static void quickSort(Comparable[] data, int min, int max) {
- int mid;
- if (min < max) {
- mid = findPartition(data, min, max);
- quickSort(data, min, mid - 1);
- quickSort(data, mid + 1, max);
- }
- }
- // quickSort的支撐方法
- private static int findPartition(Comparable[] data, int min, int max) {
- int left;
- int right;
- Comparable temp;
- Comparable partitionelement;
- // 以第一個元素爲分割元素(不一定是data[min])
- partitionelement = data[min];
- left = min;
- right = max;
- while (left < right) {
- // 從左邊找到比partitionelement大的數就跳出循環
- while (data[left].compareTo(partitionelement) <= 0 && left < right) {
- left++;
- }
- // 從右邊找到比partitionelement小的數就跳出循環
- while (data[right].compareTo(partitionelement) > 0) {
- right--;
- }
- // 交換左邊比partitionelement大的與右邊比partitionelement小的元素
- if (left < right) {
- temp = data[left];
- data[left] = data[right];
- data[right] = temp;
- }
- }
- // 當left>=right的時候,交換分割元素與data[right]的位置(把分割元素放到去一個比較中間的位置)
- temp = data[min];
- data[min] = data[right];
- data[right] = temp;
- return right;
- }
策略:首先在數組中任意選擇一個元素作爲分割元素,上面的例子是選擇第一個數。然後開 始分割數組,把比這個分割元素小的值都移到它的左邊,比他大的都移到它的右邊。然後遞歸調用這個方法,對左右兩部分排序,類似地從左邊選擇一個分割元素, 把左邊比它小的排在它的左邊,比它大的排在它的有...然後到右邊。直到只剩下一個元素時(即不能再分割時)完成排序。
這種排序需要3個參數,第一個爲對象數組,第2個是數組索引的起始位置,第3個是數組索引的結束位置。
下面對這幾種排序的實現和性能進行測試
- public static void main(String[] args) {
- long start = System.currentTimeMillis();
- Random rand = new Random();
- Integer array[] = new Integer[40000];
- for(int i=0;i<40000;i++){
- array[i] = rand.nextInt(10);
- }
- // 插入排序
- // insertionSort(array);
- // 選擇排序
- // selectionSort(array);
- // 冒泡排序
- // bubbleSort(array);
- // 快速排序
- quickSort(array, 0, 39999);
- System.out.println(Arrays.toString(array));
- long end = System.currentTimeMillis();
- System.out.println("時間差:" + (end - start) + "毫秒");
- }
結論:
對有4萬個(1~9)隨機數的數組進行排序,在我的機器上運行,使用遞歸的快速查詢約1秒,插入查詢用5秒左右,選擇查詢11秒+,冒泡算法要幾乎14秒。當然要查詢的對象越少,他們之間效率的差別就越小。在排序對象的數量不多時,用順序查詢會比較方便與直觀。