常用排序算法(冒泡排序,快速排序,堆排序)

這是常用的一些基本排序算法.

數據結構

先定義公共的數據結構

typedef int KeyType;
typedef struct {
    KeyType  key;
}RecType;
typedef RecType SeqSortList[MAXSIZE+1];//表中的0元素空着或用作哨兵單元

一般把數組中的第0個位置預留出來,就是爲了作爲哨兵,進行兩個數據進行交換時,可以把哨兵作爲臨時變量進行交換.

公共方法:

//打印集合
void  logSort(SeqSortList R,int start,int end);

打印數組集合: R:數組對象. start:開始打印的角標. end:結束打印的角標.

void  logSort(SeqSortList R,int start,int end){
    printf("[");
    for(int i=start;i<=end;i++){
        if(i==start){
            printf("%d",R[i].key);
        }else{
            printf(",%d",R[i].key);
        }
    }
    printf("]");
}

堆排序

1.排序思想

第一步創建大根堆(或者小根堆)
第二步步驟:取堆頂關鍵字與無序區最後一個元素互換.然後剩下的無序區元素進行第一步.
最後直到無序區元素爲1個,結束循環.

注意:大根堆或者小根堆是完全二叉樹.

2.完全二叉樹

待排序數組R=[10,15,56,6,33,5,26,8]
完全二叉樹:
在這裏插入圖片描述

3.創建大根堆

這裏以建立大根堆爲例:
若 結點位置爲: i ,其左孩子位置: 2*i, 其右孩子位置: 2*i+1.
先判斷左孩子和右孩子的最大值, 如果 i 結點的值小於其左孩子和右孩子的最大值,則 i 結點與最大值位置的互換.否則不用調整.

最後就是 i 結點的值大於其左孩子(2*i)的值和右孩子(2*i+1)的值.

注意:堆排序中大根堆(小根堆)是完全二叉樹爲基礎.

一共有8個結點,

在這裏插入圖片描述
建大根堆第一步: 從第4個結點(6)判斷是否需要調整:
第4個結點小於其左孩子,需要調整,6和8互換.結果爲下圖.

在這裏插入圖片描述
建大根堆第二步:第3個結點(56)判斷是否需要調整:
第3個結點大於其左孩子和右孩子,不需要調整.結果爲下圖:

在這裏插入圖片描述
建大根堆第三步: 第2個結點(15)判斷是否需要調整:
第2個結點小於其右孩子,則15月33互換位置.結果爲下圖:

在這裏插入圖片描述

建大根堆第四步: 第1個結點(10)判斷是否需要調整:
第1個結點小於其左孩子和右孩子,右孩子大於左孩子,所以10與56互換位置.
互換後:10又小於其右孩子26,所以10月26位置還要互換一次.結果爲下圖:
在這裏插入圖片描述
到此大根堆已經建立完成:

4.堆排序

大根堆創建完成後,其實還是無序的.
下面一步就是取關鍵字的步驟.
1.每次都取堆頂,然後與無序區的最後一個記錄進行交換,
2.然後再將剩下的區域再走一次創建大根堆.
然後重複1和2步驟,直到無序區的結點個數爲1

下面進行取關鍵字的步驟:

無序區用:
在這裏插入圖片描述

有序區用:
在這裏插入圖片描述

在這裏插入圖片描述
取關鍵字第一步:取堆頂元素56,與無序區最後一個元素6互換位置(56加入了有序區),然後再把剩下的無序區走一遍建立大根堆,結果爲下面的圖.

在這裏插入圖片描述
取關鍵字第二步:取堆頂元素33,與無序區最後一個元素10互換位置(33加入了有序區),然後再把剩下的無序區走一遍建立大根堆,結果爲下面的圖.
在這裏插入圖片描述
取關鍵字第三步:取堆頂元素26,與無序區最後一個元素5互換位置(26加入了有序區),然後再把剩下的無序區走一遍建立大根堆,結果爲下面的圖.
在這裏插入圖片描述
取關鍵字第四步:取堆頂元素15,與無序區最後一個元素6互換位置(15加入了有序區),然後再把剩下的無序區走一遍建立大根堆,結果爲下面的圖.
在這裏插入圖片描述
取關鍵字第五步:取堆頂元素10,與無序區最後一個元素5互換位置(10加入了有序區),然後再把剩下的無序區走一遍建立大根堆,結果爲下面的圖.
在這裏插入圖片描述
取關鍵字第六步:取堆頂元素8,與無序區最後一個元素6互換位置(8加入了有序區),然後再把剩下的無序區走一遍建立大根堆,結果爲下面的圖.

在這裏插入圖片描述
取關鍵字第七步:取堆頂元素6,與無序區最後一個元素5互換位置(6加入了有序區),然後再把剩下的無序區走一遍建立大根堆,結果爲下面的圖.

在這裏插入圖片描述
此時無序區就剩下了一個結點,取關鍵字結束.
最後的有序的完全二叉樹爲:
在這裏插入圖片描述
有序的數組爲順序讀取二叉樹[5,6,8,10,15,26,33,56].

5.算法運行結果

在這裏插入圖片描述

6.算法實現


//一次堆排序.
void Sift(SeqSortList R,int i,int h){
    //將R[i...h]調整爲大堆跟,假定R[i]的左右孩子均滿足堆的性質
    int j;
    RecType x=R[i];
    j=2*i;
    while (j<=h) {//當R[i]的左孩子不爲空時執行循環
        if(j<h && R[j].key<R[j+1].key)
            j++;//若右孩子的關鍵字較大,j++.置爲右孩子角標
        if(x.key>R[j].key)//表示當前x大於其左右孩子,找到了x的位置
            break;
        R[i]=R[j];//將大於i位置的置爲雙親的位置
        i=j;j=2*i;//然後修改i和j,重新調整指向,重新查找位置.
    }
    R[i]=x;
}
//堆排序
void HeapSort(SeqSortList R,int n){
    //對R[1..h]進行堆排序,設置R[0]爲暫存單元
    int i;
    //這裏的循環是建立大堆
    for(i=n/2;i>=1;i--)
        Sift(R, i, n);
    printf("大根堆:");
    logSort(R, 1, n);
    printf("\n");
    for(i=n;i>1;i--){//對R[1..h]進行n-1此堆排序,取出了n-1個數據,剩下的一個數據,自然就是最小的.
        R[0]=R[1];R[1]=R[i];R[i]=R[0];//每次都R[1]爲最大的,將R[1]與R[i]進行互換,
        Sift(R, 1, i-1);//然後再讓//下面是對R[1...i-1]建立堆排序
        printf("取關鍵字%d後的完全二叉樹:",R[0]);
        logSort(R, 1, n);
        printf("\n");
    }
}
    int  n=8;
    SeqSortList R;
    R[0].key=0;//哨兵
    R[1].key=10;
    R[2].key=15;
    R[3].key=56;
    R[4].key=6;
    R[5].key=33;
    R[6].key=5;
    R[7].key=26;
    R[8].key=8;
            printf("無序R=");
            logSort(R, 1, n);
            printf("\n");
            HeapSort(R, n);
            printf("有序R=");
            logSort(R, 1, n);
            printf("\n");

雙向冒泡排序

1.排序思想

例如:有n個數據.
1.遍歷[n--------->1]位置.從底部(n位置)往上查找最小的值,放到第1位置.
2.遍歷[2--------->n]位置,從第2個位置往底部查找最大值,放到第n位置.
3.遍歷[n-1------>2]位置,從第n-1位置往上查找最小值,放在第2位置.
4.遍歷[3-------->n-1]位置,從第2個位置往底部查找最大值,放到第n-1位置.

最多遍歷n-1次,就可以排序好, 不管查找最大值和最小值的時,判斷的時候也許就和鄰數據做了交換,所以有時候遍歷不到n-1次,剩下的就已經排序好了(除非原來的待排序的數組是逆序的),就沒必要再繼續判斷,這個是通過一個標記變量來判斷的.

每一次循環前置爲flag=0.如果做了數據交換,則置爲flag=1. 每次循環前,如果flag=0,則繼續循環,如果flag==1,則結束循環,表明此時數組已經是有序的了.

2.算法例子和運行結果

在這裏插入圖片描述
i=1.表示進行的兩次循環,從1-n 和n-1 兩次遍歷, 分別查找最小值和最大值.
然後分別放在了兩邊有序的位置.
從例子上可以看出,這是循環了2*2次,中間待循環的數組已經是有序的了.

3.算法的實現

//雙向冒泡排序
void DboubbleSort(SeqSortList R,int n){
    //從下往上查找小的,從上往下找大的
    int i=1,j;
    int NoSwap;
    NoSwap=1;//表示是否有交換,默認的爲有交換,無序
    printf("[有序][無序][有序]\n");
    while(NoSwap){
        NoSwap=0;//先設置爲無交換
        //從下往上找最小的
        for(j=n-i+1;j>=i+1;j--)
            if(R[j].key<R[j-1].key){
                R[0]=R[j];
                R[j]=R[j-1];
                R[j-1]=R[0];
                NoSwap=1;
            }
        //此時第i個位置存儲的是小的,
        //那麼從第i+1個位置開始往下找,最大的.
        //從上往下找最大的
        for(j=i+1;j<=n-i;j++)
            if(R[j].key>R[j+1].key){
                R[0]=R[j];
                R[j]=R[j+1];
                R[j+1]=R[0];
                NoSwap=1;
            }
        
        printf("i=%d\n",i);
        logSort(R, 1, i);
        logSort(R, i+1, n-i);
        logSort(R, n-i+1, n);
        printf("\n");
        i=i+1;
    }
}

快速排序

1.排序思想

1.每次取待排序數組的第一個的數據(x),然後將當前的待排序的數組進行劃分.劃分成三部分. (小於x的數組)(x)(大於x的數組).
2.然後再對 (小於x的數組)和 (小於x的數組)進行劃分.就這樣遞歸調用.

最關鍵的就是劃分方法:
劃分的思想:請查看圖
在這裏插入圖片描述

2.算法例子和運行結果

在這裏插入圖片描述

3.算法的實現


//快速排序中的--劃分方法
int Partition(SeqSortList R,int i,int j){
    //=========
    printf("劃分前:");
    int low=i;//只爲打印用
    int height=j;////只爲打印用
    logSort(R, low, height);
    printf("\n");
    //========

    //對R[i]~R[j]區間內的記錄進行一次劃分排序
    RecType x=R[i];//用區間第一個記錄爲基準
    while(i<j){
         //先從後面往前找第一個小於基準的值,賦給R[i]
        while (i<j && R[j].key>=x.key) {
            j--;
        }
        //找到了小於基準的值,賦給R[i]
        if(i<j){
            R[i]=R[j];
            //這裏i++,下面從從前面往後找第一個大於x的時候,從後一個開始查找
            i++;
        }
        //然後從R[i]位置開始向後找,第一個大於x的值,賦給R[j].
        while(i<j&& R[i].key<=x.key){
            i++;
        }
        if(i<j){
            R[j]=R[i];
            j--;
        }
    }
    R[i]=x;
      //=========
      printf("劃分後:");
      logSort(R, low, i-1);
      logSort(R, i, i);
      logSort(R, i+1, height);
      printf("\n");
      //========
    return i;
}
//快速排序方法
void QuickSort(SeqSortList R,int low,int hight){
    int  p;
    if(low<hight){
        p=Partition(R, low, hight);
        QuickSort(R, low, p-1);
        QuickSort(R, p+1, hight);
    }
}

冒泡排序

直接選擇排序

直接插入排序

請查看這篇文章:
https://blog.csdn.net/forwardyzk/article/details/53640901

箱排序

基數排序

請查看這篇文章:
https://blog.csdn.net/forwardyzk/article/details/102935430

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