數據結構之排序

北航軟件工程專業考研991數據結構總結:

八、內排序
1.排序的基本概念,各種內排序方法的基本原理和特點,包括排序過程中進行的元素之間的比較次數,排序總趟數、排序穩定性以及時間複雜度與空間複雜度計算;
2.插入排序法(含折半插入排序法);
3.選擇排序法;
4.(起)泡排序法;
5.謝爾(Shell)排序法;
6.快速排序法;
7.堆積(Heap)排序法,包括堆積的定義與構造;

1、插入排序法

原理:第i趟將第 i+1 個元素插入到前面已經排序的 i 個元素序列中,並保持排序

void InsertSort(int *a, int n)
{
    int i, j, temp;
    
    for(i = 1; i < n-1; i++)
    {
        temp = a[i];
        j = i-1;
        while(j >= 0 && a[j] > temp)    //依次向前比較,將大於i的數後移
            a[j+1] = a[j--];
            
        a[j+1] = temp;
    }
}

最好的情況下,需要比較n-1次,即已經排序好,時間複雜度就是O(n)
最壞的情況下,需要比較你n(n-1)/2次,即逆序排列,時間複雜度爲O(n^2)

穩定排序

2、選擇排序法

原理:第i趟排序,從後面的 n-i+1 個元素中選擇一個最小的,放置到 n-i+1 個未排序元素的最前面

void SelectSort(int *a, int n)
{
    int i, j, d, temp;
    
    for(i = 0; i < n-1; i++)
    {
        d = i;        
        for(j = i + 1; j < n; j++)    //從後面的元素中找到最小的元素
        {
            if(a[j] < a[d])
                d = j;
        }
        if(d != i)
        {
            temp = a[i];
            a[i] = a[d];
            a[d] = temp;
        }
    }
}

無論好壞,都需要比較n(n-1)/2次,時間複雜度爲O(n^2)
穩定排序

3、泡排序

原理:第i趟排序,將前面的 n-i+1 個元素從第一個開始左右比較,如果左邊大於右邊,則進行互換,否則不進行換序
      當沒有出現換序的時候,則排序完成,或者只有第一個元素的時候排序完成
      
void BubbleSort(int *a, int n)
{
    int i, j, temp, flag;
    flag = 1;
    i = n-1;
    while(i > 0 && flag == 1)
    {
        flag = 0;
        for(j = 0; j < i; j++)
        {
            if(a[j] > a[j+1])
            {
                temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
                flag = 1;
            }
        }
        i--;
    }
}

當元素已經排序好的時候,不需要交換  比較次數爲 n-1 時間複雜度O(n)
當元素沒有排序時,至少交換一個
當元素第一個爲最大,剩餘的都是排序時,交換n-1次
最壞的情況下,比較次數爲 n(n-1)/2 時間複雜度O(n^2)
適用於序列基本有序的情況下
穩定排序

*****************
前面的這3中排序簡直就是玩
搞定後面的這些才牛逼呢
*****************

4、謝爾Shell排序  縮小增量排序法

設置一個間隔d,每隔間隔d對元素進行排序
然後縮小間隔d,繼續進行排列
知道d < 1 排列完成

時間複雜度介於 nlog2n 到 n^2之間
最外層的趟數爲log2n
非穩定排序

算法:

void SelectSort(int *a, int n)
{
    int i,j,d,temp, flag;
    d = n;
    while(d > 1)
    {
        d = d/2;
        do
        {
            for(i = 0; i < n - d; i++)
            {
                j = i + d;
                if(a[i] > a[j]
                {
                    temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                    flag = 1;
                }
            }
        }while{flag != 0);
    }
}

5、快速排序法

原理:確定一個比較元素,讓所有大於該數的元素排列在它後面,小於它的數排列在它後面,在對長度大於1的前後子列進行遞歸運算

時間複雜度:O(nlog2n)

非穩定排序

算法步驟:

void QuickSort(int *a, int n)
{
    Quick(a, 0, n-1);
}

void Quick(int *a, int s, int t)
{
    int i, j, temp;
    if(s < t)
    {
        i = s;
        j = t+1;
        
        while(1)
        {
            do i++;
            while(!(a[s] < a[i] || i == t));
            
            do j--;
            while(!(a[s] > a[j]) || j == s);
            
            if(i < j)
            {
                temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }else{
                break;
            }
        }
        
        temp = a[s];
        a[s] = a[j];
        a[j] = temp;
        
        Quick(a, s, j-1);
        Quick(a, j+1, t);
    }
}

兩邊出發  前小後大

6、堆積(Heap)排序,包括堆積的定義與構造

堆積的定義

使得一組序列滿足 ki >= k2i 並且 ki >= k(2i+1)  大頂堆積

就是一個完全二叉樹,使用順序存儲方式(數組)進行存儲

排序:

首先將一個序列構造成大頂堆積,然後,將收尾互換,再將前 n-i+1 個元素構造大頂堆積,繼續進行

問題是,如何構造大頂堆積呢?

先看一個左右序列已經是大頂堆積的,也就是一個大頂堆積首位互換之後的剩下的堆進行構造

將頂元素與子元素進行比較,如果小於最大的那個數,進行互換,再在子樹中繼續進行比較

這樣,構造一個初始大頂堆積,可以從中間的 n/2 個元素開始,遞減的調用上述方式即可

代碼:


void Adjust(int *a, int i, int n)
{
    int j, temp;
    j = 2 * i;
    temp = a[i];
    while(j <= n)
    {
        if(j < n && a[j] < a[j+1])
            j++;
            
        if(temp > a[j])
            break;
            
        a[j/2] = a[j];    //將子樹中較大的元素移到父樹中去
        j = 2 * j;
    }
    a[j/2] = temp;
}

注意:上述是理論代碼,不是C語言的可執行代碼,因爲C語言中,數組從1開始,所以,j和i的關係應該是 j = 2 * i + 1;  i = (j - 1)/2;

可執行的代碼:

void Adjust(int *a, int i, int n)
{
    int j, temp;
    j = 2 * i + 1;
    temp = a[i];
    while(j <= n)
    {
        if(j < n && a[j] < a[j+1])
            j++;
            
        if(temp > a[j])
            break;
            
        a[(j-1)/2] = a[j];    //將子樹中較大的元素移到父樹中去
        j = 2 * j + 1;
    }
    a[(j-1)/2] = temp;
}

堆積排序主函數:

void HeapSort(int *a, int n)
{
    int i = 0;
    for(i = n / 2; i >= 1; i--)
    {
        Adjust(a, i, n);
    }
    
    for(i = n-1; i >= 1; i--)
    {
        int temp = a[i+1];
        a[i+1] = a[1];
        a[1] = temp;
        Adjust(a, 1, i);
    }
}

同樣,這是理論的函數,C語言中的可執行函數如下:

void HeapSort(int *a, int n)
{
    int i = 0;
    for(i = (n-1) / 2; i >= 0; i--)
    {
        Adjust(a, i, n-1);
    }
    
    for(i = n-2; i >= 0; i--)
    {
        int temp = a[i + 1];
        a[i + 1] = a[0];
        a[0] = temp;
        Adjust(a, 0, i);
    }
}

非穩定  nlog2n


總結一下:

插入排序法
    
    最好的情況下,需要比較n-1次,即已經排序好,時間複雜度就是O(n)
    最壞的情況下,比較次數爲n(n-1)/2,即逆序排列,時間複雜度O(n^2)
    穩定的排序
    
選擇排序法

    O(n^2) 與好壞無關
    穩定排序

泡排序

    已經排序的情況下不需要交換
    最大的元素在首,其他排序,交換n-1次 
    穩定排序
    適用於序列基本有序的情況下
    
    
謝爾Shell排序

    需要一個間隙d,劃分子列,
    介於 nlog2n 與 n^2之間
    不穩定排序
    循環嵌套三層

快速排序法

    選擇一個元素作爲比較依據,前大後小,兩端出發
    nlog2n
    不穩定排序

堆積排序

    構造堆積
    nlog2n
    不穩定排序

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