數據結構-排序一

開場白

本文主要講解一下排序方法:程序員

  • 冒泡
  • 選擇
  • 插入
  • 希爾
  • 堆排序

如下代碼中數組都是從下標1開始的,下標0用做哨兵;排序代碼都是進行升序排列。算法

公共代碼部分

展現部分公共代碼,瞭解後會對後面代碼的閱讀減小一下障礙。shell

typedef struct {
    int r[MAXSIZE + 1];
    int length;
}SqList;

void swap(SqList *L, int i, int j) {
    int temp = L->r[i];
    L->r[i] = L->r[j];
    L->r[j] = temp;
}

void print(SqList L) {
    int i;
    for (i = 1; i<L.length; i++) {
        printf("%d,", L.r[i]);
    }
    printf("%d", L.r[i]);
    printf("\n");
}
複製代碼

冒泡

冒泡排序應該是全部程序員最喜歡的排序方法,由於代碼可讀性很是高。數組

初級冒泡

//冒泡排序初級版本
void BubbleSort0(SqList *L) {
    for (int i = 1; i< L->length; i++) {
        for (int j = i+1; j<= L->length; j++) {
            if (L->r[i] > L->r[j]) {
                swap(L, i, j);
            }
        }
    }
}
複製代碼

正宗的冒泡排序算法

咱們平時所說的冒泡其實不是正宗的,真正的正宗冒泡實際上是這樣的bash

void BubbleSort(SqList *L) {
    for (int i = 1; i< L->length; i++) {
        for (int j = L->length - 1; j>=i; j--) {
            if (L->r[j] > L->r[j+1]) {
                swap(L, j, j+1);
            }
        }
    }
}
複製代碼

內部for循環時從後往前的順序。其實也沒什麼特別的,也很好理解。markdown

冒泡排序優化

增長一個flag標誌函數

void BubbleSort2(SqList *L) {
    Status flag = TRUE;
    for (int i = 1; i< L->length && flag; i++) {
        flag = FALSE;
        for (int j = L->length - 1; j>=i; j--) {
            if (L->r[j] > L->r[j+1]) {
                swap(L, j, j+1);
                flag = TRUE;
            }
        }
    }
}
複製代碼

其實就是能夠提早跳出循環。優化

選擇排序

void SelectSort(SqList *L) {
    for (int i = 1; i < L->length; i++) {
        int min = i;
        for (int j = i+1; j <= L->length; j++) {
            if (L->r[min] > L->r[j]) {
                min = j;
            }
        }
        if (i!=min) {
            swap(L, i, min);
        }
    }
}
複製代碼

也比較簡單,邏輯上和冒泡有點區別,先找到最小或者最大值,記錄下來,內部循環接受後,與外部循環的i進行交換。spa

直接插入排序

void InsertSort(SqList *L) {
    int temp = 0;
    for (int i = 2; i<=L->length; i++) {
        if (L->r[i]<L->r[i-1]) {
            temp = L->r[i];
            int j;
            for (j=i-1; L->r[j]>temp; j--) {
                L->r[j+1]=L->r[j];
            }
            L->r[j+1]=temp;
        }
    }
}
複製代碼

數組從前到後遍歷中,當前元素小於它前面的元素(前面全部的元素已經排序完成),將當前元素出入到前面的已排完的順序中。code

希爾排序

void shellSort(SqList *L) {
    int increment = L->length;
    
    do {
        increment = increment/3+1;
        for (int i = increment+1; i<=L->length; i++) {
            if (L->r[i] < L->r[i-increment]) {
                L->r[0] = L->r[i];
                int j;
                for (j = i-increment; j > 0 && L->r[0]<L->r[j]; j-=increment) {
                    L->r[j+increment] = L->r[j];
                }
                
                L->r[j+increment] = L->r[0];
            }
        }
        
    } while (increment > 1);
}
複製代碼

希爾排序的特色是修改了比較元素之間的步長距離,以前的排序都是臨近的兩個元素進行比較,而希爾排序是可讓編寫者根據需求自行修改比較步長。這樣有一個優勢,根據步長的不一樣與業務需求,會獲得不一樣的時間複雜度。

堆排序

什麼叫堆

堆是具備下⾯性質的徹底⼆叉樹:每一個結點的值都⼤於或等於其左右孩⼦結點的值,稱爲⼤頂堆;如圖1;或者每一個結點的值都⼩於等於其左右孩⼦的結點的值,稱爲⼩頂堆,如圖2.

堆排序

堆排序(Heap Sort) 就是利⽤堆(假設咱們選擇⼤頂堆)進⾏排序的算法.它的基本思想:

  1. 將待排序的序列構成⼀個⼤頂堆,此時,整個序列的最⼤值就堆頂的根結點,將它移⾛(其實就是將其與堆數組的末尾元素交換, 此時末尾元素就是最⼤值);
  2. 而後將剩餘的n-1個序列從新構成⼀個堆,這樣就會獲得n個元素的次⼤值,如此重複執⾏,就能獲得⼀個有序序列了

示例

構造初始堆

2.
3.
4.

交換

2.
3.
4. 後續過程,繼續進⾏調整,交換,如此反覆進⾏,最終使得整個序列有序

堆排序思路

  • 將⽆序序列構建成⼀個堆,根據升序降序需求選擇⼤頂堆或⼩頂堆
  • 將堆頂元素與末尾元素交換,將最⼤元素「沉」到數組末端;
  • 從新調整結構,使其滿⾜堆定義,而後繼續交換堆頂元素與當前末尾元素,反覆執⾏調整+交換步驟,直到整個序列有序;

代碼

//大頂堆調整函數
void HeapAdjust(SqList *L, int s, int m) {
    int temp = L->r[s];
    for (int j = 2*s; j <= m; j*=2) {
        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;
}

//堆排序
void HeapSort(SqList *L) {
    for (int i=L->length/2; i>0; i--) {
        HeapAdjust(L, i, L->length);
    }
    
    for (int i = L->length; i>1; i--) {
        swap(L, 1, i);
        HeapAdjust(L, 1, i-1);
    }
}
複製代碼

以上排序算法的運行方法

#define N 9
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, 排序算法\n");
    
    int i;
    int d[N]={9,1,5,8,3,7,4,6,2};
    //int d[N]={9,8,7,6,5,4,3,2,1};
    //int d[N]={50,10,90,30,70,40,80,60,20};
    SqList l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10;
   
    for(i=0;i<N;i++)
        l0.r[i+1]=d[i];
    
    l0.length=N;
    l1=l2=l3=l4=l5=l6=l7=l8=l9=l10=l0;
    
    printf("排序前:\n");
    print(l0);
    printf("\n");
    
    //1.初級冒泡排序
    printf("初級冒泡排序:\n");
    BubbleSort0(&l0);
    print(l0);
    printf("\n");
    
    //2.冒泡排序
    printf("冒泡排序:\n");
    BubbleSort(&l1);
    print(l1);
    printf("\n");
    
    //3.冒泡排序優化
    printf("冒泡排序(優化):\n");
    BubbleSort2(&l2);
    print(l2);
    printf("\n");
    
    //4.選擇排序
    printf("選擇排序:\n");
    SelectSort(&l3);
    print(l3);
    printf("\n");
    
    //5.直接插入排序
    printf("直接插入排序:\n");
    InsertSort(&l4);
    print(l4);
    printf("\n");
    
    //6.希爾排序
    printf("希爾排序:\n");
    shellSort(&l5);
    print(l5);
    printf("\n");
    
    //7.堆排序
    //注意:執行對排序時更換一下數據源. 這裏我爲何要這組數據,緣由是爲了與下標個位數字講解時進行區分;由於在這個算法講解過程,出現了不少下標的相關計算.
    /* int d[N]={50,10,90,30,70,40,80,60,20}; */
    printf("堆排序:\n");
    HeapSort(&l6);
    print(l6);
    printf("\n");
    
    printf("\n");
    return 0;
}
複製代碼

相關文章
相關標籤/搜索