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;
}
//因為遞歸的使用,非常大的數組用快速排序,直接插入排序是簡單排序中性能最好的
經過優化的快速排序是性能最好的排序算法
從最好情況看,選擇冒泡和直接插入排序
從最壞情況看,選擇堆排序與歸并排序
從穩定性來看,選擇歸并排序
如果執行算法的軟件所處的環境非常在乎內存使用量的多少,選擇堆排序等