1. 程式人生 > >八大排序(一):四種簡單的排序(直接插入排序、希爾排序、氣泡排序、選擇排序)

八大排序(一):四種簡單的排序(直接插入排序、希爾排序、氣泡排序、選擇排序)

一、直接插入排序

直接插入排序基本思想是每一步將一個待排序的記錄,插入到前面已經排好序的有序序列中去,直到插完所有元素為止。
直接插入排序演算法的運作如下:
假設有一組無序序列 R0, R1, … , RN-1。
(1) 我們先將這個序列中下標為 0 的元素視為元素個數為 1 的有序序列。
(2) 然後,依次把 R1, R2, … , RN-1 插入到這個有序序列中。所以,我們需要一個外部迴圈,從下標 1 掃描到 N-1 。
(3) 接下來描述插入過程。假設這是要將 Ri 插入到前面有序的序列中。由前面所述,我們可知,插入Ri時,前 i-1 個數肯定已經是有序了。
所以我們需要將Ri 和R0 ~ Ri-1 進行比較,確定要插入的合適位置。這就需要一個內部迴圈,我們一般是從後往前比較,即從下標 i-1 開始向 0 進行掃描。

結合下圖可以更直觀的理解
這裡寫圖片描述

直接插入排序:

    public float[] insertSort(float[] array) {
        int j;
        for (int i = 1; i < array.length; i++) {
            j = i - 1;
            float temp = array[i];
            while (j >= 0 && array[j] > temp) {
                array[j + 1] = array[j];
                j--;
            }
            array
[j + 1] = temp; } return array; }

當資料正序時,執行效率最好,每次插入都不用移動前面的元素,時間複雜度為O(N)。
當資料反序時,執行效率最差,每次插入都要前面的元素後移,時間複雜度為O(N*2)。

二、希爾排序

希爾排序跟直接插入排序很相似,其實對比下程式碼就知道就是在直接插入排序的基礎上增加了一個分組的功能。結合下圖理解一下:

這裡寫圖片描述

在上面這幅圖中,有一個大小為 10 的無序序列:
第一趟排序中,通過計算gap1=N/2(即10/2),將10個元素分為5組,即(9,4),(1,8),(2,6),(5,3),(7,5),然後對每組內的元素進行插入排序。
第二趟排序中,把上次的 gap 縮小一半,即 gap2 = gap1 / 2 = 2 (取整數)。這樣每相隔距離為 2 的元素組成一組,可以分為 2 組。分組後依舊對每組的元素進行插入排序。
第三趟排序中,再次把 gap 縮小一半,即gap3 = gap2 / 2 = 1。 這樣相隔距離為 1 的元素組成一組,即只有一組。再進行一次插入排序。
需要注意的是,圖中有兩個相等數值的元素 5 和 5 。我們可以清楚的看到,在排序過程中,兩個元素位置交換了。所以,希爾排序是不穩定的演算法。

希爾排序:

    public float[] shellSort(float[] array) {
        int arrLength = array.length;
        for (int step = arrLength >> 1; step > 0; step >>= 1) {
            for (int i = step; i < arrLength; i += step) {// 從這裡開始跟插入排序比較
                int j = i - step;
                float temp = array[i];
                while (j >= 0 && array[j] > temp) {
                    array[j + step] = array[j];
                    j -= step;
                }
                array[j + step] = temp;
            }
        }
        return array;
    }

三、氣泡排序

氣泡排序演算法的運作如下:
1.比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
2.對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素會是最大的數。
3.持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。

可以結合下圖理解(一輪排序):
這裡寫圖片描述

氣泡排序:

    public float[] bubbleSort(float[] array) {
        for (int i = 1; i < array.length; i++) {
            for (int j = 0; j < array.length - i; j++) {
                if (array[j] > array[j + 1]) {
                    float temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
        return array;
    }

原陣列本身就是有序的,僅需n-1次比較就可完成
若是倒序,比較次數為 n-1+n-2+…+1=n(n-1)/2,交換次數和比較次數等值。所以,其時間複雜度依然為ON*2)

四、選擇排序

選擇排序是簡單直觀的一種演算法,基本思想為每一趟從待排序的資料元素中選擇最小(或最大)的一個元素作為首元素,直到所有元素排完為止,擇排序是不穩定的排序方法(比如序列[5, 5, 3]第一次就將第一個[5]與[3]交換,導致第一個5挪動到第二個5後面)。

選擇排序演算法的運作如下:
(1)從待排序序列中,找到最小的元素;
(2)如果最小元素不是待排序序列的第一個元素,將其和待排序序列的第一個元素互換;
(3)從餘下的 N - i 個元素中,找出關鍵字最小的元素,重複(1)、(2)步,直到排序結束。

選擇排序:

    public float[] selectSort(float[] array) {
        for (int i = 0; i < array.length - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < array.length; j++) {
                if (array[j] < array[minIndex]) {
                    minIndex = j;
                }
            }
            float temp = array[minIndex];
            array[minIndex] = array[i];
            array[i] = temp;
        }
        return array;
    }

簡單選擇排序的比較次數與序列的初始排序無關。 假設待排序的序列有 N 個元素,則比較次數總是N (N - 1) / 2。
而移動次數與序列的初始排序有關:
當序列正序時,移動次數最少,為 0.
當序列反序時,移動次數最多,為3N (N - 1) / 2。
所以,綜合以上,簡單排序的時間複雜度為 O(N*2)。