排序演算法-合併排序(C語言實現)
都說“演算法是程式的靈魂”,而排序是計算機儲存控制方面不能沒有的操作。它在資料的存取,查詢搜尋,資料統計這些基礎資料操作方面有著重要的應用。所以排序演算法是必須是很有研究的。
這次,我學習的是-歸併排序演算法。據說該演算法是馮諾依曼發明的。這個排序演算法比起直接的冒泡法的優勢,在於它的時間複雜度上的優勢O(N*logN)。冒泡法要N!當N很大時候,時間差異很大。當然歸併排序在空間複雜度上是絕對的劣勢O(N*logN),冒泡法幾乎不多佔用額外記憶體。
合併演算法的描述:
順序描述(演繹)
1. 將要排序的N個數據分成N/2個組(1,2)(3,4)(5,6)、、、(n,n-1)分別排好序
2. 將分別排好序的N/2個組,分成(N/2)/2個組,然後分別排序。排序的方式:建立一個容器,然後第一個位置(最小),是來自兩個已經排好序的序列中“首位”較小的那個,抽取了誰,誰的首位就要讓位給後一個,然後再選次最小的放在第二個位置
3. 將分別排好序的(N/2)/2個組,接著分別合併,然後排序、、、
4. 直到合併成最大的陣列
這就是為什麼這個叫“合併排序”了。
逆向描述(遞迴)
1. 如果要排序的序列已經是部分“排好”了,那麼接下來的排序,我們就沒必要用“最蠢”方法遍歷對比了,而只要比較最多N/2次就行。
2. 所以我們先把部分排好序,然後利用上面提到的方法排序,就得到全排序了。
3. 部分排序可以採用同樣方法,先將小部分排好序,這樣就可以用上面的方式排序目前的部分
4. 、、、同樣的方式直到兩兩比較。然後就用直接方法排好,當然稍加註意把這個也劃歸到上面的排序中也可以。
採用遞迴或是迴圈的方式不是區分是否是歸併演算法的關鍵。但是一般這個演算法我們都用遞迴實現。上面說的空間複雜度也是在遞迴中產生的。
#define length 100 //在這裡進行巨集定義,因為load.h裡用到。當然可以在load.h的開始定義
#include <stdio.h>
#include "load.h"
int list[length];
void sort(int *l,int n);//宣告
int main()
{
int i;
load(list); //將隨機資料從.txt載入,假定都是整型資料
sort(&list[0],(int)length); //呼叫排序函式,把隨機數列從小到大排好
printf("the sorted list is :\n");
for(i=0;i<length;i++)
{
printf ("%d\t",list[i]);
}
printf("\n");
return 0;
}
/*合併排序演算法的實現*/
void sort(int *l,int n)
{
int tp[n];
int i = 0,j = 0,c = 0;
int temp1,temp2,temp;
int *left,*right;
temp1 =(int)(n/2); //中間分割的方法
temp2 = n - temp1;
left = &l[0];
right= &l[temp1];
if (2 < n)
{
sort(left,temp1); //遞迴呼叫本函式去解決更小的問題
sort(right,temp2);
while ( temp1 != i && temp2 != j )
{
if (left[i]<=right[j])
{
tp[c] = left[i];
i++;
}
else
{
tp[c] = right[j];
j++;
}
c++;
}
if (temp1 == i)
{
for(c=0;c<temp1+j;c++)
{
l[c]=tp[c];
}
}
else
{
for(c=i;c<temp1;c++)
l[c+j]=l[c];
for(c=0;c<temp2+i;c++)
{
l[c]=tp[c];
}
}
}
if (n = 1)
if (n = 2)
{
if (l[0]<=l[1])
return;
else
{
temp = l[0];
l[0] = l[1];
l[1] = temp;
}
}
}