1. 程式人生 > >結構與算法(3)-----冒泡、選擇、插入排序算法

結構與算法(3)-----冒泡、選擇、插入排序算法

工作 圖片 另一個 idt 最大的 情況 可能 insert 循環嵌套

1、冒泡排序

  該名詞的由來很好理解,一般河水中的冒泡,水底剛冒出來的時候是比較小的,隨著慢慢向水面浮起會逐漸增大。

  冒泡算法的運作規律如下:

  ①、比較相鄰元素如果第一個比第二個大,就交換他們兩個

  ②、對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數第一波冒泡完成)。

  ③、針對所有的元素重復以上的步驟,除了最後一個

  ④、持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。

  技術分享圖片

  技術分享圖片

  代碼如下:

/* 冒泡排序 */
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]; 1、 第一個元素放到臨時位置 array[j] = array[j + 1]; 2、 將第二個元素的位置,往前調
array[j
+ 1] = temp; 3、 在將原來寄存在臨時位置的元素,取出來放到第二個位置上 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.jkqiang.demo;

//選擇排序
public class ChoiceSort {
    public static int[] sort(int[] array) {
        // 總共要經過N-1輪比較
        for (int i = 0; i < array.length - 1; i++) {
            int min = i;    //第一輪認為第一個元素最小        
步驟1、找到最小元素
// 每輪需要比較的次數 for (int j = i + 1; j < array.length; j++) { //從第二個元素開始取值,與最小元素比較 if (array[j] < array[min]) { min = j; // 記錄目前能找到的最小值元素的下標 } }
//步驟2、 將找到的最小值和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.jkqiang.demo;
//直接插入排序
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)小。 

結構與算法(3)-----冒泡、選擇、插入排序算法