這是常用的一些基本排序算法.
數據結構
先定義公共的數據結構
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