插入排序

插入排序

插入排序的基本原則是,將一個待排序的元素,按照排序規則,插入到前面已經排好序的一組元素的適當位置,直到元素全部插入爲止。根據尋找插入位置的不同方式,可將插入排序分爲直接插入排序折半插入排序,還有一種對直接插入排序的優化方案希爾排序

直接插入排序

直接插入排序的是,將一組待排序的元素第一個元素看做是有序的,然後從第二個元素開始,將他插入到前面排好序的一組元素的合適的位置。結合下面 的一組數據說吧

第一步:從元素8開始,往前查找,發現8比34小,則將8插入到34前面

第二步:從元素64開始,往前查找,發現沒有比64小的元素,則64不動

第三步:從元素51開始,往前查找,發現沒有比64小的元素,則64不動

。。。依次類推
在這裏插入圖片描述
代碼實現

/**
 * 插入排序
 *
 * @Author HXY
 * @Date 2020/2/25
 */
public class InsertionSort {
    public static void sort(int[] arr) {
        int j = 0;
        for (int i = 1; i < arr.length; i++) {
            int tmp = arr[i];
            for (j = i; j > 0 && tmp < arr[j - 1]; j--) {
                arr[j] = arr[j - 1];
            }
            arr[j] = tmp;
        }
    }
}

直接插入排序算法在最好的情況下,也就是這組數據已經排好序的情況下,內層for循環的條件每次判定失敗,只執行外層的fort循環,時間複雜度可以看做O(N)。而在最壞情況下,也就是一組要從小到大排序的數據,初始化恰恰是從大到小排序的,這個時候時間複雜度是O(N2),一般情形下也是O(N2)。

折半插入排序

折半插入排序就是在直接插入排序的尋找插入點這個步驟進行了優化,將原來的遍歷優化成折半查找。代碼如下:

public static void sort(int[] arr) {
    int j = 0;
    for (int i = 1; i < arr.length; i++) {
        int low = 0, high = i - 1, mid = 0;
        int tmp = arr[i];
        // 尋找插入點
        while (low <= high) {
            mid = (low + high) / 2;
            if (arr[i] < arr[mid]) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        // 將插入點後的元素統一後移,然後將元素插入插入點
        for (j = i; j > high + 1; j--) {
            arr[j] = arr[j - 1];
        }
        arr[high + 1] = tmp;
    }
}

當數據較多時,折半插入排序平均性能優於直接插入排序,但是比直接插入排序的最好情況是要差的。因此當初始化序列接近有序時,用直接插入排序性能更好。這兩個都是穩定的排序方式

希爾排序

結合上面的討論我們得知,當數據量相對較小或者序列基本有序時,插入排序的效率是比較高的。因此希爾大佬在這兩個點上對插入排序進行了優化,發明了希爾排序。希爾排序的主要思想就是

  1. 將一個大的序列通過一個整數gap(增量)拆分成幾組數據量小的子序列,先對這一個個子序列進行插入排序
  2. 經過上面這一步後,這個大的序列基本有序了,這個時候在執行一遍插入排序

具體的過程參見下圖
在這裏插入圖片描述
我們採取gap=3這個增量將初始化序列在邏輯上分爲三個子序列,然後對這三個子序列進行插入排序,這一步後這個序列基本有序,我們在控制gap=1,這時候就相當於對這個大的序列進行一次插入排序。

代碼如下:

// 希爾排序
public static void shellSort(int[] arr) {
    int j = 0;
    // 1. 分組
    for (int gap = arr.length / 2; gap > 0; gap /= 2) {
        // 2. 相當於遍歷分出來的幾個組
        for (int i = gap; i < arr.length; i++) {
            int tmp = arr[i];
            // 對組內的元素排序
            for (j = i; j >= gap && tmp < arr[j - gap]; j -= gap) {
                arr[j] = arr[j - gap];
            }
            arr[j] = tmp;
        }
    }
}

希爾排序的時間複雜度與這個增量的取用關係較大,至今無人能對一般情形下的時間複雜度給出證明,我這種菜鳥就暫不研究了。另外這是一種不穩定的排序方式。

Github源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章