算法#09--用簡單的思維理解選擇、插入、冒泡和希爾排序

選擇排序

1.原理

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。

2.特點

①運行時間與輸入無關。

無論輸入初始狀態如何,是否有序,都需要遍歷數組來找出最小的元素。其他算法則更善於利用輸入的初始狀態來優化時間。

②數據移動次數最小。

如果某個元素位於正確的最終位置上,則它不會被移動。選擇排序每次交換一對元素,它們當中至少有一個將被移到其最終位置上,因此對n個元素的表進行排序總共進行至多n-1次交換。在所有的完全依靠交換去移動元素的排序方法中,選擇排序屬於非常好的一種。

3.代碼

    public static void selection_sort(int[] arr) {
        int i, j, min, temp, len = arr.length;
        for (i = 0; i < len - 1; i++) {
            min = i;
            for (j = i + 1; j < len; j++)
                if (arr[min] > arr[j])
                    min = j;
            temp = arr[min];
            arr[min] = arr[i];
            arr[i] = temp;
        }
    }

4.複雜度

①交換次數O(n): 最好情況是,已經有序,交換0次;最壞情況是,逆序,交換n-1次

②比較次數O(n^2): (n-1)+(n-2)+…+1=n*(n-1)/2

③賦值次數O(n):0到3(n-1)次之間

5.概述

最差時間複雜度 О(n²)

最優時間複雜度 О(n²)

平均時間複雜度 О(n²)

最差空間複雜度 總共О(n), 需要輔助空間O(1)

動態圖:

選擇排序的示例動畫。紅色表示當前最小值,黃色表示已排序序列,藍色表示當前位置。

插入排序

1.原理

通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。

步湊如下:

①從第一個元素開始,該元素可以認爲已經被排序

②取出下一個元素,在已經排序的元素序列中從後向前掃描

③如果該元素(已排序)大於新元素,將該元素移到下一位置

④重複步驟3,直到找到已排序的元素小於或者等於新元素的位置

⑤將新元素插入到該位置後

⑥重複步驟②~⑤

如果比較操作的代價比交換操作大的話,可以採用二分查找法來減少比較操作的數目。該算法可以認爲是插入排序的一個變種,稱爲二分查找插入排序。

2.特點

直接插入排序是一種穩定的排序。對部分有序的數組十分高效,也很適合小規模數組。

3.代碼

    public static void insertion_sort( int[] arr ) {
        for( int i=0; i<arr.length-1; i++ ) {   
            for( int j=i+1; j>0; j-- ) {
                if( arr[j-1] <= arr[j] )
                    break;
                int temp = arr[j];
                arr[j] = arr[j-1];
                arr[j-1] = temp;
            }
        }
    }

另一個版本:

    public static void insertion_sort(int[] arr)
    {
        for (int i = 1; i < arr.length; i++ ) {
            int temp = arr[i];
            for (int j = i - 1; j >= 0 && arr[j] > temp; j-- ) {
                arr[j + 1] = arr[j];
            }
            arr[j + 1] = temp;
    }

4.複雜度

①交換次數O(0): 不需要

②比較次數O(n^2): 最好情況是,已經有序,需(n-1)次即可次;最壞情況是,逆序,需n(n-1)/2次

③賦值次數O(n^2):比較操作的次數加上(n-1)次

平均來說插入排序算法複雜度爲O(n^2)因而,插入排序不適合對於數據量比較大的排序應用。但是,如果需要排序的數據量很小,例如,量級小於千,那麼插入排序還是一個不錯的選擇。

5.概述

最差時間複雜度 О(n²)

最優時間複雜度 О(n)

平均時間複雜度 О(n²)

最差空間複雜度 總共О(n), 需要輔助空間O(1)

動態圖:

使用插入排序爲一列數字進行排序的過程:

冒泡排序

1.原理

它重複地遍歷過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。遍歷數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。

步湊如下:

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

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

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

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

2.特點

儘管這個算法是最簡單瞭解和實現的排序算法之一,但它對於少數元素之外的數列排序是很沒有效率的。

3.代碼

    public static void bubble_sort(int[] arr) {
        int i, j, temp, len = arr.length;
        for (i = 0; i < len - 1; i++)
            for (j = 0; j < len - 1 - i; j++)
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
    }

4.複雜度

①交換次數O(n^2)

②比較次數O(n^2): 可以原地排序。

③賦值次數O(0):不需要,交換就可以了。

冒泡排序是與插入排序擁有相等的運行時間,但是兩種算法在需要的交換次數卻很大地不同。在最好的情況,冒泡排序需要O(n^2)次交換,而插入排序只要最多O(n)交換。冒泡排序的實現通常會對已經排序好的數列拙劣地運行O(n^2),而插入排序在這個例子只需要O(n)個運算。

5.概述

最差時間複雜度 О(n²)

最優時間複雜度 О(n)

平均時間複雜度 О(n²)

最差空間複雜度 總共О(n), 需要輔助空間O(1)

動態圖:

希爾排序

1.原理

將數組列在一個表中並對列排序(用插入排序)。重複這過程,不過每次用更長的列來進行。最後整個表就只有一列了。將數組轉換至表是爲了更好地理解這算法,算法本身僅僅對原數組進行排序(通過增加索引的步長,例如是用i += step_size而不是i++)。

例如,假設有這樣一組數[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我們以步長爲5開始進行排序,我們可以通過將這列表放在有5列的表中來更好地描述算法,這樣他們就應該看起來是這樣:

13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10

然後我們對每列進行排序:

10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45

將上述四行數字,依序接在一起時我們得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].這時10已經移至正確位置了,然後再以3爲步長進行排序:

10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45

排序之後變爲:

10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94

最後以1步長進行排序(此時就是簡單的插入排序了)。

2.特點

步長的選擇是希爾排序的重要部分。只要最終步長爲1任何步長序列都可以工作。算法最開始以一定的步長進行排序。然後會繼續以一定步長進行排序,最終算法以步長爲1進行排序。當步長爲1時,算法變爲插入排序,這就保證了數據一定會被排序。

3.代碼

    public static void shell_sort(int[] arr) {
        int gap = 1, i, j, len = arr.length;
        int temp;
        while (gap < len / 3)
            gap = gap * 3 + 1; // <O(n^(3/2)) by Knuth,1973>: 1, 4, 13, 40, 121, ...
        for (; gap > 0; gap /= 3)
            for (i = gap; i < len; i++) {
                temp = arr[i];
                for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
                    arr[j + gap] = arr[j];
                arr[j + gap] = temp;
            }
    }

4.複雜度

5.概述

最差時間複雜度 根據步長序列的不同而不同,最好時爲О(n(logn)^2)

最優時間複雜度 O(n)

平均時間複雜度 根據步長序列的不同而不同

最差空間複雜度 О(n)

動態圖:

發佈了45 篇原創文章 · 獲贊 21 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章