《大話數據結構》筆記二(排序)

1 冒泡排序(優化)
2 選擇排序
3 直接插入排序
4 希爾排序
5 堆排序
6 歸并排序(優化)
7 快速排序(優化)

#define MAXSIZE 10000  /* 用于要排序數組個數最大值,可根據需要修改 */
typedef struct
{
    int r[MAXSIZE+1];   /* 用于存儲要排序數組,r[0]用作哨兵或臨時變量 */
    int length;         /* 用于記錄順序表的長度 */
}SqList;

1 冒泡排序

兩兩比較相鄰的數據,如果反序則交換,直到沒有反序的記錄
//改進冒泡排序
//在性能上有一些提升,避免因已經有序的情況下的無意義循環判斷

void maoPao(SqList *L){
    Status flag = TRUE;
    //若flag為true則退出循環
    for (int i = 1; i < L->length && flag; i++)
    {
        flag = FALSE;
        for (int j = L->length - 1; j >= 1; j--)
        {
            if (L->r[j] > L->r[j+1])
            {
                int temp = L->r[i];
                L->r[i] = L->r[j];
                L->r[j] = temp;
                flag = TRUE;
            }
        }
    }
}

2 選擇排序

//通過n-i次關鍵字間的比較,從n-i+1個數據中選出關鍵字最小的數據,并和第i(1<=i<=n)個數據交換

void xuanZe(SqList *L){
    int min;
    for (int i = 1; i < L->length; i++)
    {
        //將當前下標定義為最小值下標
        min = i;
        //循環當前下標之后的所有數據
        for (int j = i+1; j <= L->length; j++)
        {
            //j=2 L[1]>L[2] 9>1
            //min=2
            //j=3 L[2]>L[3] 1>5
            //min不變,還是2
            if (L->r[min] > L->r[j])
            {
                min = j;
            }
        }
        //全比較過后,交換值
        if (i != min)
        {
            int temp=L->r[i];
            L->r[i]=L->r[min];
            L->r[min]=temp;
        }
    }
}

3 直接插入排序

//將一個數據插入到已經排好序的有序表中,得到一個新的有序表

//r[6] = {0,5,3,4,6,2}
void chaRu(SqList *L){
    int i,j;
    for (i = 2; i < L->length; i++)
    {
        //i=2,053462,3<5
        //i=3,035462,4<5接著執行
        if (L->r[i] < L->r[i-1])
        {
            //設置哨兵
            //r0 = 3
            L->r[0] = L->r[i];
            //j=1 5>3成立,走循環體
            //j=0 3>3不成立,出循環,此時j為0
            for (j=i-1; L->r[j] > L->r[0]; j--)
            {
                //r2=5
                L->r[j+1] = L->r[j];
            }
            //r1=3
            L->r[j+1] = L->r[0];
        }
    }
}
4 希爾排序

//實際上是優化的直接插入排序,采取跳躍分割的策略,將相距某個"增量"的記錄組成一個子序列,保證子序列內分別進行直接插入排序結果基本有序(大的基本在后面)
//時間復雜度為O(n3/2),比O(n2)要好點

void xiEr(SqList *L){
    int i,j;
    int zeng = L->length;
    do{
        //增量序列
        zeng = zeng/3 + 1;
        //分段排序,與直接插入排序相同
        for (i = zeng+1; i <= L->length; i++)
        {
            //判斷是否交換
            if (L->r[i] < L->r[i-zeng])
            {
                L->r[0] = L->r[i];
                for (j = i-zeng; j>0 && L->r[j] > L->r[0]; j-=zeng)
                {
                    L->r[j+zeng] = L->r[j];
                }
                L->r[j+zeng] = L->r[0];
            }
        }
    //增量為1時,停止循環
    } while (zeng > 1);
}
5 堆排序

//對簡單選擇排序進行的一種改造,時間復雜度為O(nlogn),比冒泡,選擇,插入的O(n2)好很多
//堆是一種近似完全二叉樹:利用完全二叉樹的深度是(log2n)+1的特性
//每個結點的值都大于或等于左右孩子結點的值,為大頂堆
//每個結點的值都小于或等于左右孩子結點的值,為小頂堆
//將待排序的序列構造成一個大頂堆,整個序列的最大值就是堆頂的根結點,將它移走,然后將剩余的n-1個序列重新構造成一個大頂堆,反復執行
//就是將堆數組的根結點與末尾元素交換,末尾元素就變成最大值了

//將數組調整為大頂堆
void daDingDui(SqList *L,int s,int m);
void dui(SqList *L){
    int i;
    //把有子節點的結點調整成大頂堆
    for (i = L->length/2 ; i>0 ; i--)
    {
        daDingDui(L, i, L->length);
    }
    for (i = L->length; i>1 ; i--)
    {
        //將堆頂數據和當前未經排序子序列的最后一個數據交換
        //把最大值換到尾巴處
        int temp=L->r[i];
        L->r[i]=L->r[1];
        L->r[1]=temp;
        //排除最后一個最大數據,再重新調整成大頂堆
        daDingDui(L, 1, i-1);
    }
}

void daDingDui(SqList *L,int s,int m){
    int temp,j;
    temp= L->r[s];
    //沿關鍵字較大的孩子結點向下篩選
    //根據完全二叉樹的按層序遞增排列特點
    //從j=2s開始,j=m結束
    for (j=2*s ; j<=m ; j*=2)
    {
        //j為左孩子下標,j+1為右孩子下標,找出最大的
        if (j<m && L->r[j] < L->r[j+1])
        {
            ++j;
        }
        //比較根結點大小
        if (temp >= L->r[j])
        {
            break;
        }
        //把最大值給根結點
        L->r[s] = L->r[j];
        s = j;
    }
    //把剛開始保存的較小值給子結點
    L->r[s] = temp;
}

6 歸并排序

//歸并--將兩個或兩個以上的有序表組合成一個新的有序表
//2路歸并排序:n個數據,兩兩歸并,直到得到一個長度為n的有序序列
//總的時間復雜度為O(nlogn),空間復雜度為O(n+logn)
//比較占用內存,但是穩定,效率高

//歸并函數
void realGuiBing(int SR[],int TR1[],int s,int t);
//排序函數
void Merge(int SR[],int TR[],int s,int m,int t);

//調用歸并函數
void guiBing(SqList *L){
    realGuiBing(L->r,L->r,1,L->length);
}

//遞歸函數,將 SR[s...t] 歸并排序為 TR1[s...t]
void realGuiBing(int SR[],int TR1[],int s,int t){
    int m;
    int TR2[MAXSIZE +1];
    //相等就不用排序了
    if (s == t)
    {
        TR1[s] = SR[s];
    }
    else
    {
        //將SR[s...t]平分為SR[s...m]和SR[m+1...t]
        m = (s+t)/2;
        //遞歸將SR[s...m]歸并為有序的TR2[s...m]
        realGuiBing(SR,TR2,s,m);
        //遞歸將SR[m+1...t]歸并為有序的TR2[m+1...t]
        realGuiBing(SR,TR2,m+1,t);
        //將TR2[s...m]和TR2[m+1...t]歸并到TR1[s...t]
        Merge(TR2,TR1,s,m,t);
    }
}

//關鍵函數,排序并合并
//將有序的SR[i...m] 和 SR[m+1...n]歸并為有序的TR[i...n]
void Merge(int SR[],int TR[],int s,int m,int t){
    int j,k,l;
    //將SR中數據由小到大歸并入TR
    for (j = m+1, k = s; s <= m && j <= t; k++)
    {
        if (SR[s] < SR[j])
        {
            TR[k] = SR[s++];
        }
        else
        {
            TR[k] = SR[j++];
        }
    }
    //將剩余的SR[s...m]復制到TR
    if (s <= m)
    {
        for (l = 0; l <= m-s; l++)
        {
            TR[k+1] = SR[s+1];
        }
    }
    //將剩余的SR[j...t]復制到TR
    if (j <= t)
    {
        for (l = 0; l < t-j; l++)
        {
            TR[k+1] = SR[j+1];
        }
    }
}

//優化歸并排序,非遞歸實現

//非遞歸增加的函數
void MergeAdd(int SR[], int TR[],int s,int n);
//歸并排序
void guiBing2(SqList *L){
    //申請內存空間
    int *TR = (int *)malloc(L->length *sizeof(int));
    int k = 1;
    while (k < L->length)
    {
        MergeAdd(L->r, TR, k, L->length);
        //子序列長度加倍
        k = 2*k;
        MergeAdd(TR, L->r, k, L->length);
        //加倍
        k = 2*k;
    }
}

void MergeAdd(int SR[], int TR[],int s,int n){
    int i = 1;
    int j;
    while (i <= n - 2 * s + 1)
    {
        //兩兩歸并
        Merge(SR, TR, i, i + s - 1, i + 2 *s -1);
        i = i +2 *s;
    }
    //歸并最后兩個序列
    if (i < n-s+1)
    {
        Merge(SR, TR, i, i+s-1, n);
    }
    //剩下的單數直接并入
    else
    {
        for (j = i; j <= n; j++)
        {
            TR[j] = SR[j];
        }
    }
}

//希爾排序為直接插入排序的升級,屬于插入排序類
//堆排序為簡單選擇排序的升級,屬于選擇排序類
//快速排序為冒泡排序的升級,屬于交換排序類

7 快速排序

//通過一趟排序將待排數據分割成獨立的兩部分,其中一部分key均比另一部分key小,分別對這兩部分記錄繼續排序,最終完成排序
//時間復雜度平均為O(nlogn)

//快速排序
void realKuaiSu(SqList *L,int low,int high);
//找到樞軸值
int fenKai(SqList *L ,int low,int high);
//調用快速排序
void kuaiSu(SqList *L){
    realKuaiSu(L,1,L->length);
}
//快速排序
void realKuaiSu(SqList *L,int low,int high){
    int pivot;
    if (low < high)
    {
        //將L->r[low...high]一分為二,算出樞軸值pivot
        pivot = fenKai(L,low,high);
        //對低子表遞歸排序
        realKuaiSu(L,low,pivot-1);
        //對高子表遞歸排序
        realKuaiSu(L,pivot+1,high);
    }
}

//找到樞軸值,交換順序表L中子表的數據
//將選取的樞軸值不斷變換,將比它小的換到它的左邊,比它大的換到它的右邊,它也在交換中不斷更改自己的位置,直到完全滿足這個要求為止
int fenKai(SqList *L ,int low,int high){
    int pivotkey;
    //用子表的第一個數據做樞軸數據
    pivotkey = L -> r[low];
    //從表的兩端交替向中間掃描
    while (low < high)
    {
        //比樞軸數據小的數據交換到低端
        while (low < high && L->r[high] >= pivotkey)
        {
            high--;
            int temp=L->r[low];
            L->r[low]=L->r[high];
            L->r[high]=temp;
        }
        //將比樞軸大的數據交換到高端
        while (low < high && L->r[low] <= pivotkey)
        {
            low++;
            int temp=L->r[low];
            L->r[low]=L->r[high];
            L->r[high]=temp;
        }
    }
    return low;
}

//快速排序優化

//1.選取樞軸pivotkey的優化
//三數取中法:取左,中,右三個數據先排序,將中間數作為樞軸
//九數取中法
int getPivotkey(SqList *L,int high,int low){
    int pivotkey;
    //計算數組中間元素的下標
    int m = low + (high - low)/2;
    //交換左右端數據,保證左端較小
    if (L->r[low] > L->r[high])
    {
        int temp=L->r[low];
        L->r[low]=L->r[high];
        L->r[high]=temp;
    }
    //交換中間與右端數據,保證中間較小
    if (L->r[m] > L->r[high])
    {
        int temp=L->r[m];
        L->r[m]=L->r[high];
        L->r[high]=temp;
    }
    //交換中間與左端數據,保證左端較小
    if (L->r[m] > L->r[low])
    {
        int temp=L->r[m];
        L->r[m]=L->r[low];
        L->r[low]=temp;
    }
    //L.r[row]為三個關鍵字中間值
    pivotkey = L->r[low];
    return pivotkey;
}

//2.優化不必要的交換
int youHuaKuaiSu(SqList *L,int low,int high){
    //取到樞軸
    int pivotkey = getPivotkey(L, high, low);
    //將樞軸備份
    L->r[0] = pivotkey;
    //從表的兩端交替向中間掃描
    while (low < high)
    {
        //比樞軸數據小的數據交換到低端
        while (low < high && L->r[high] >= pivotkey)
        {
            high--;
            //采用替換不是交換
            L->r[low] = L->r[high];
        }
        //將比樞軸大的數據交換到高端
        while (low < high && L->r[low] <= pivotkey)
        {
            low++;
            //采用替換不是交換
            L->r[high] = L->r[low];
        }
    }
    //將樞軸數值替換回L.r[low]
    L->r[low] = L->r[0];
    //返回樞軸所在位置
    return low;
}

//因為遞歸的使用,非常大的數組用快速排序,直接插入排序是簡單排序中性能最好的

經過優化的快速排序是性能最好的排序算法
從最好情況看,選擇冒泡和直接插入排序
從最壞情況看,選擇堆排序與歸并排序
從穩定性來看,選擇歸并排序
如果執行算法的軟件所處的環境非常在乎內存使用量的多少,選擇堆排序等

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容

  • 第一章 緒論 什么是數據結構? 數據結構的定義:數據結構是相互之間存在一種或多種特定關系的數據元素的集合。 第二章...
    SeanCheney閱讀 5,807評論 0 19
  • 一、 單項選擇題(共71題) 對n個元素的序列進行冒泡排序時,最少的比較次數是( )。A. n ...
    貝影閱讀 9,167評論 0 10
  • 因為之前就復習完數據結構了,所以為了保持記憶,整理了一份復習綱要,復習的時候可以看著綱要想具體內容。 樹 樹的基本...
    牛富貴兒閱讀 6,966評論 3 10
  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    蟻前閱讀 5,213評論 0 52
  • 概述:排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,740評論 0 15