1. 程式人生 > >幾種重要的排序方法

幾種重要的排序方法

list 希爾 按順序 bre sta init pre 原來 循環

1.插入排序(insertion sort)

技術分享圖片

如圖所示,將需要排序的序列,分成已排序的部分,和未排序的部分。

循環中,每一次就將當前叠代到的,未排序的第一個元素,插入到在已排序部分中的適當位置。

2.選擇排序(selection sort)

技術分享圖片

如圖所示,首先便利所有未排序的元素,找出最大的一個,然後與數組中的最後一個交換。

下一次叠代就從未排序的元素中,找出最大的一個,與數組中倒數第二個交換,以此類推。

3. 希爾排序(shell sort)

技術分享圖片

希爾排序,主要是將各元素按一個遞減的增量,來對元素分組排序,如圖所示,一開始increment為5,則將元素分為5組,每組3個,元素在組內先按大小排序好。

然後increment按(increment = increment / 3 + 1)的形式進行遞減,則第二次叠代increment為3,則將元素分為3組,再在組內進行排序。直到increment小於等於1。

具體算法:

void shell_sort() {
    int increment, start;
    increment = array.count;
    do {
        increment = increment / 3 + 1;
        for (start = 0; start < increment; start++) {
            sort_interval(start, increment);
        }
    } while(increment > 1);
}

4. 歸並排序(merge sort)

技術分享圖片

歸並排序是采用分治法的一種。通過直接將數組對半分,然後分成2部分數組,進行遞歸,直到數組中元素為一個,則函數直接返回,而父函數就將兩個數組中的元素進行比較,合並成為一個已經排好序的數組。

具體算法:

void recursive_merge_sort(Node*& sub_list) {
    if (sub_list != NULL && sub_list -> next != NULL) {
        Node *second_half = divide_from(sub_list);
        recursive_merge_sort(sub_list);
        recursive_merge_sort(second_half);
        sub_list = merge(sub_list, second_half);
    }
}

5. 快排(quick sort)

快排的核心,其實也是分治法。

通過選定一個pivot。

技術分享圖片

然後將數組分成大於這個pivot的和小於這個pivot的兩組。

技術分享圖片

再遞歸,分別給這兩組再找pivot及分組,直到組內元素為1。則快排結束。

6. 堆排序

堆定義:堆分為最大堆和最小堆,其實就是完全二叉樹。最大堆要求節點的元素都要不小於其孩子,最小堆要求節點元素都不大於其左右孩子,兩者對左右孩子的大小關系不做任何要求。

在一個二叉堆中,位置k的結點的父結點的位置為k/2 向下取整,而他的兩個子結點的位置分別為2k和2k+1。

一個例子:

技術分享圖片

上圖是一個最大堆。

堆主要有兩個操作:

(1)插入:

a. 往堆中的某個空節點插入一個元素,則要用該元素與該節點的兩個子節點的較大一個進行比較。

b. 如果元素大於該較大節點,則元素能夠放在這個位置。

否則,將這個較大節點,上浮到元素原來想存放的位置。然後元素的位置移到這個較大節點,再重復a

參考代碼思路:

void insert_heap(int current, int low, int high) {
    int large = 2 * low + 1; // large是low的左子節點
    while (large <= high) {
        if (large < high && entry[large] < entry[large + 1])
            large++; // 確保large是兩個子節點中的較大一個
        if (current >= entry[large])
            break; // 可以放在low這個位置
        else {
            entry[low] = entry[large]; // 交換較大節點到low
            low = large; // low指向較大節點的位置
            large = 2 * low + 1; // large變為low的左子節點
        }
    }
    entry[low] = current; // 放在low的位置
}

推排序最主要的幾個步驟:

(1)初始化一個最大堆

將一個無序的數組,變化成一個最大堆。

方法:從最後一個子元素的父元素開始,按順序,把非葉子節點的元素插入,判斷其是否適合該位置。

參考代碼:

void init_heap() {
    int low;
    for (low = count / 2 - 1; low >=0; low--) {
        int current = entry[low];
        insert_heap(current, low, count - 1);
    }
}

(2)進行堆排序

把最大堆的堆頂元素出堆,放在最後一個元素(數組尾部)的位置,然後插入這個最後的元素進堆裏。這樣,每次出堆的都是當前堆的最大值,所以就能完成排序。

技術分享圖片

參考代碼:

void heap_sort() {
    int current, last_unsorted;
    init_heap();
    for (last_unsorted = entry.count - 1; last_unsorted > 0; last_unsorted--) {
        current = entry[last_unsorted]; // 記錄最後的元素
        entry[last_unsorted] = entry[0]; // 把堆頂的元素放到sorted中
        insert_heap(current, 0, last_unsorted - 1); // 把最後的元素放進堆中,重新構建堆。
    }
}

7. 基數排序

基數排序(Radix Sort)是桶排序的擴展,它的基本思想是:將整數按位數切割成不同的數字,然後按每個位數分別比較。
具體做法是:將所有待比較數值統一為同樣的數位長度,數位較短的數前面補零。然後,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以後, 數列就變成一個有序序列。

技術分享圖片

8. 各種排序的空間、時間復雜度以及穩定性

技術分享圖片

幾種重要的排序方法