1. 程式人生 > >資料結構:直接插入排序 & 希爾排序 & 選擇排序 & 堆排序 & 氣泡排序 & 快速排序 & 歸併排序

資料結構:直接插入排序 & 希爾排序 & 選擇排序 & 堆排序 & 氣泡排序 & 快速排序 & 歸併排序

一、什麼是排序

排序就是將一組雜亂無章的資料按照一定的次序組織起來,此次序可以是升序也可以是降序

二、為什麼需要進行排序

為了滿足一些需求,比如在比較學生的成績時,我們就需要給所有學生的成績排一個順序,這樣才方便我們檢視學生的名詞,所以說排序就是為了給我們生活中的事提供方便。

三、資料表

資料表就是待排序資料元素的有限集合

四、排序碼&主排序碼&次排序碼

(1)排序碼
排序時需要按照一定的規則,才能夠排序,而排序碼就是規則,比如比較兩個學生的名次時,可以用綜合成績來比較,此綜合成績就是排序碼
(2)主排序碼 & 次排序碼


如果一個數據表中各個元素的排序碼有多個,比如學生的屬性有學號、成績、姓名、班級等,在進行排序時,這些屬性都可以作為排序碼,如果使用一個排序碼排序出來的結果是唯一的,那此排序碼就是主排序碼,如果使用一個排序碼排序出來的結果不唯一,那此排序碼就是次排序碼。就像學生,使用學號進行排序時,排序結果是唯一的,那學號就是主排序碼,因為一個學生的學號是唯一的,而如果使用成績進行排序時,排序結果可能不唯一,因為一個分數可能會有好幾個人,誰的名次在前,誰的名次在後,都是不確定的,那成績就是次排序碼

五、排序演算法的穩定性

如果在一個排序表中,有兩個元素R[i]==R[j],它們的排序碼K[i]==K[j],在排序之前,元素R[i]在元素R[j]的前面,排序之後,元素R[i]還是元素R[j]的前面,那麼此演算法就是穩定的,否則就認為這個演算法不穩定
例如對{1,2,4,7,8,9,5,5,6}進行排序,資料表中有兩個5,下標為6的5在下標為7的5前面,如果排序之後,下標為6的5還是在下標為7的5前面,那就說此排序演算法是穩定的,否則是不穩定的。

ps:在排序時,如果牽扯到間隔的交換兩個元素的話,那此演算法就是不穩定的,例如直接選擇排序演算法就是不穩定的。如果是挨著交換的話,那就認為此演算法是穩定的,例如氣泡排序,是緊挨著的元素的兩兩交換,所以冒泡演算法是穩定的。例如在進行快速排序就是不穩定的,因為會牽扯到間隔的元素的交換。

六、內部排序&外部排序

(1)內部排序
當在進行排序時,需要進行排序的資料可以一次性的載入到記憶體
(2)外部排序
與內部排序相反,需要進行排序的資料需要多次的載入到記憶體,不能一次性的載入到記憶體。

七、排序演算法

1.直接插入排序

演算法描述

當一個序列進行排序時,把序列分成兩個部分,有序和無序,預設第一個元素是有序的,依次把無序序列中的元素通過與有序序列中元素的比較,搬移到有序序列中。
ps:B樹在插入資料時,就是使用的插入排序

(1)簡單版的插入排序(對資料按升序進行排序)

方法:比較一個元素搬移一個元素

void InsertSort(int array[], int sz)//插入排序
{
    int i = 0;
    int end = 0;//標記有序序列中的最後一個元素
    for (i = 1; i < sz; i++){
        end = i - 1;//預設陣列的第一個元素是有序的
        int key = array[i];//key儲存的是待排序的元素
        while (key < array[end] && end >= 0){
            array[end + 1] = array[end];
            end--;
        }
        //在while迴圈中,也可以把key<array[i]寫為key<=array[i],但
        //是如果這樣寫的話,會使此演算法不穩定
        array[end + 1] = key;
    }
}

(2)使用二分查詢來尋找插入位置(對資料按升序進行排序)

優點:在尋找插入位置時,比較的次數少
方法:使用二分尋找位置,然後搬移

//使用二分查詢查詢插入位置的插入排序
void InsertSort_Binary(int array[], int sz)
{
    for (int i = 1; i < sz; i++){
        int key = array[i];
        //1.第一步:使用2分查詢演算法找到待插入元素需要插入的位置
        int left = 0;
        int right = i-1;
        while (left <= right){
            int mid = left + ((right - left) >> 1);
            if (key < array[mid])
                right = mid - 1;
            else
                left = mid + 1;

        }
        //2.第二步:插入待插入的元素(搬移left後面到i之前所有的元素)
        right = i - 1;
        //注意right==left的元素也需要搬移,因為此時在上面迴圈的結束
        //條件是left>right,所以left多往後面動了一下
        while (right >= left){
            array[right + 1] = array[right];
            right--;
        }
        array[left] = key;
    }
}

ps:這兩個方法搬移的次數一樣,但是使用二分查詢,會使比較的次數少,從而提高排序的效率
(3)複雜度

時間複雜度:O(N) 到 O(N^2)之間
空間複雜度:O(1)

(4)插入排序的應用場景

應用場景:

a.需要排序的資料序列已經接近有序;
b.資料量少

最差場景

給的資料序列是降序,但是需要一個升序序列

最優場景

給的資料序列是降序,正好需要的也是一個降序序列

(5)插入排序演算法是穩定的排序演算法

2.希爾排序

ps:此排序時插入排序的升級版,當插入排序處理的資料量大時,採用希爾排序
演算法描述
這裡寫圖片描述
(1)程式碼

void ShellSort(int array[], int sz)//希爾排序
{
    int gap = sz / 3 + 1;//gap是控制分組的,例如gap=3,那
    //就是3個元素為一組
    int i = 0;
    while (gap){
        for ( i = gap; i < sz; i++){
            int end = i-gap;//標記有序序列中的最後一個元素
            int key = array[i];//待插入元素
            while (key < array[end] && end >= 0){
                array[end + gap] = array[end];
                end -= gap;
            }
            array[end + gap] = key;
        }
        --gap;
    }
}

(2)希爾排序的應用場景

資料量大的時候,可以採用希爾排序

(3)希爾排序不穩定,因為排序時元素是隔著交換的
(4)複雜度

時間複雜度

與gap的取值規則有關,時間複雜度一般集中在N^1.25到1.6N^1.25之間,當gap=(元素個數)/3+1時,希爾排序的效率最高

空間複雜度:O(1)