標籤:最大的 部落格 縮小 隨機數 介紹 注意 -- str 已耗用時間
目錄
- 1、冒泡排序
- 2、選擇排序
- 3、插入排序
- 4、總結
上一篇部落格我們實現的數組結構是無序的,也就是純粹按照插入順序進行排列,那麼如何進行元素排序,本篇部落格我們介紹幾種簡單的排序演算法。
回到頂部1、冒泡排序
這個名詞的由來很好理解,一般河水中的冒泡,水底剛冒出來的時候是比較小的,隨著慢慢向水面浮起會逐漸增大,這物理規律我不作過多解釋,大家只需要瞭解即可。
冒泡演算法的運作規律如下:
①、比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
②、對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數(也就是第一波冒泡完成)。
③、針對所有的元素重複以上的步驟,除了最後一個。
④、持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
代碼如下:
package com.ys.sort;public class BubbleSort {public static int[] sort(int[] array){//這裡for迴圈表示總共需要比較多少輪for(int i = 1 ; i < array.length; i++){//設定一個標記,若為true,則表示此次迴圈沒有進行交換,也就是待排序列已經有序,排序已經完成。boolean flag = true;//這裡for迴圈表示每輪比較參與的元素下標//對當前無序區間array[0......length-i]進行排序//j的範圍很關鍵,這個範圍是在逐步縮小的,因為每輪比較都會將最大的放在右邊for(int j = 0 ; j < array.length-i ; j++){if(array[j]>array[j+1]){int temp = array[j];array[j] = array[j+1];array[j+1] = temp;flag = false;}}if(flag){break;}//第 i輪排序的結果為System.out.print("第"+i+"輪排序後的結果為:");display(array);}return array;}//遍曆顯示數組public static void display(int[] array){for(int i = 0 ; i < array.length ; i++){System.out.print(array[i]+" ");}System.out.println();}public static void main(String[] args) {int[] array = {4,2,8,9,5,7,6,1,3};//未排序數組順序為System.out.println("未排序數組順序為:");display(array);System.out.println("-----------------------");array = sort(array);System.out.println("-----------------------");System.out.println("經過冒泡排序後的數組順序為:");display(array);}}
結果如下:
本來應該是 8 輪排序的,這裡我們只進行了 7 輪排序,因為第 7 輪排序之後已經是有序數組了。
冒泡排序解釋:
冒泡排序是由兩個for迴圈構成,第一個for迴圈的變數 i 表示總共需要多少輪比較,第二個for迴圈的變數 j 表示每輪參與比較的元素下標【0,1,......,length-i】,因為每輪比較都會出現一個最大值放在最右邊,所以每輪比較後的元素個數都會少一個,這也是為什麼 j 的範圍是逐漸減小的。相信大家理解之後快速寫出一個冒泡排序並不難。
冒泡排序效能分析:
假設參與比較的數組元素個數為 N,則第一輪排序有 N-1 次比較,第二輪有 N-2 次,如此類推,這種序列的求和公式為:
(N-1)+(N-2)+...+1 = N*(N-1)/2
當 N 的值很大時,演算法比較次數約為 N2/2次比較,忽略減1。
假設資料是隨機的,那麼每次比較可能要交換位置,可能不會交換,假設機率為50%,那麼交換次數為 N2/4。不過如果是最壞的情況,初始資料是逆序的,那麼每次比較都要交換位置。
交換和比較次數都和N2 成正比。由於常數不算大 O 標記法中,忽略 2 和 4,那麼冒泡排序運行都需要 O(N2) 時間層級。
其實無論何時,只要看見一個迴圈嵌套在另一個迴圈中,我們都可以懷疑這個演算法的已耗用時間為 O(N2)級,外層迴圈執行 N 次,內層迴圈對每一次外層迴圈都執行N次(或者幾分之N次)。這就意味著大約需要執行N2次某個基本操作。
回到頂部2、選擇排序
選擇排序是每一次從待排序的資料元素中選出最小的一個元素,存放在序列的起始位置,直到全部待排序的資料元素排完。
分為三步:
①、從待排序序列中,找到關鍵字最小的元素
②、如果最小元素不是待排序序列的第一個元素,將其和第一個元素互換
③、從餘下的 N - 1 個元素中,找出關鍵字最小的元素,重複(1)、(2)步,直到排序結束
代碼如下:
package com.ys.sort;public class ChoiceSort {public static int[] sort(int[] array){//總共要經過N-1輪比較for(int i = 0 ; i < array.length-1 ; i++){int min = i;//每輪需要比較的次數for(int j = i+1 ; j < array.length ; j++){if(array[j]<array[min]){min = j;//記錄目前能找到的最小值元素的下標}}//將找到的最小值和i位置所在的值進行交換if(i != min){int temp = array[i];array[i] = array[min];array[min] = temp;}//第 i輪排序的結果為System.out.print("第"+(i+1)+"輪排序後的結果為:");display(array);}return array;}//遍曆顯示數組public static void display(int[] array){for(int i = 0 ; i < array.length ; i++){System.out.print(array[i]+" ");}System.out.println();}public static void main(String[] args){int[] array = {4,2,8,9,5,7,6,1,3};//未排序數組順序為System.out.println("未排序數組順序為:");display(array);System.out.println("-----------------------");array = sort(array);System.out.println("-----------------------");System.out.println("經過選擇排序後的數組順序為:");display(array);}}
運行結果:
選擇排序效能分析:
選擇排序和冒泡排序執行了相同次數的比較:N*(N-1)/2,但是至多隻進行了N次交換。
當 N 值很大時,比較次數是主要的,所以和冒泡排序一樣,用大O表示是O(N2) 時間層級。但是由於選擇排序交換的次數少,所以選擇排序無疑是比冒泡排序快的。當 N 值較小時,如果交換時間比選擇時間大的多,那麼選擇排序是相當快的。
回到頂部3、插入排序
直接插入排序基本思想是每一步將一個待排序的記錄,插入到前面已經排好序的有序序列中去,直到插完所有元素為止。
插入排序還分為直接插入排序、二分插入排序、鏈表插入排序、希爾排序等等,這裡我們只是以直接插入排序講解,後面講進階排序的時候會將其他的。
代碼如下:
package com.ys.sort;public class InsertSort {public static int[] sort(int[] array){int j;//從下標為1的元素開始選擇合適的位置插入,因為下標為0的只有一個元素,預設是有序的for(int i = 1 ; i < array.length ; i++){int tmp = array[i];//記錄要插入的資料j = i;while(j > 0 && tmp < array[j-1]){//從已經排序的序列最右邊的開始比較,找到比其小的數array[j] = array[j-1];//向後挪動j--;}array[j] = tmp;//存在比其小的數,插入}return array;}//遍曆顯示數組public static void display(int[] array){for(int i = 0 ; i < array.length ; i++){System.out.print(array[i]+" ");}System.out.println();}public static void main(String[] args){int[] array = {4,2,8,9,5,7,6,1,3};//未排序數組順序為System.out.println("未排序數組順序為:");display(array);System.out.println("-----------------------");array = sort(array);System.out.println("-----------------------");System.out.println("經過插入排序後的數組順序為:");display(array);}}
運行結果:
插入排序效能分析:
在第一輪排序中,它最多比較一次,第二輪最多比較兩次,一次類推,第N輪,最多比較N-1次。因此有 1+2+3+...+N-1 = N*(N-1)/2。
假設在每一輪排序發現插入點時,平均只有全體資料項目的一半真的進行了比較,我們除以2得到:N*(N-1)/4。用大O標記法大致需要需要 O(N2) 時間層級。
複製的次數大致等於比較的次數,但是一次複製與一次交換的時間耗時不同,所以相對於隨機資料,插入排序比冒泡快一倍,比選擇排序略快。
這裡需要注意的是,如果要進行逆序排列,那麼每次比較和移動都會進行,這時候並不會比冒泡排序快。
回到頂部4、總結
上面講的三種排序,冒泡、選擇、插入用大 O 標記法都需要 O(N2) 時間層級。一般不會選擇冒泡排序,雖然冒泡排序書寫是最簡單的,但是平均效能是沒有選擇排序和插入排序好的。
選擇排序把交換次數降低到最低,但是比較次數還是挺大的。當資料量小,並且交換資料相對於比較資料更加耗時的情況下,可以應用選擇排序。
在大多數情況下,假設資料量比較小或基本有序時,插入排序是三種演算法中最好的選擇。
後面我們會講解進階排序,大O標記法的時間層級將比O(N2)小。
Java資料結構和演算法(三)——冒泡、選擇、插入排序演算法