程式設計基本演算法(三)

weixin_34162629發表於2011-03-25


程式設計基本演算法(一)

程式設計基本演算法(二)

程式設計基本演算法(三)

 

選擇排序

使用條件:可對比大小的集合。

演算法思想:每一趟從待排序的資料元素中選出最小(或最大)的一個元素,順序放在已排好序的數列的最後,直到全部待排序的資料元素排完。

舉例程式設計:int b[10]={77,1,65,13,81,93,10,5,23,17}

//簡單選擇排序
void SimpleSelect(int b[10])
{
int temp;
int i;
for(i=0;i<9;i++)
{
for(int j=i+1;j<9;j++)
{
if(b[i]>b[j])
{
temp
=b[i];
b[i]
=b[j];
b[j]
=temp;
}
}
}
cout
<<"the sort is:";
for(int i=0;i<10;i++)
{
cout
<<b[i]<<" ";
}
cout
<<endl;
}

效能分析:時間複雜度為On^2

 

堆排序

使用條件:可對比大小的集合。

演算法思想:其實堆排序是簡單選擇排序的一種進化,它最主要是減少比較的次數。什麼是堆?若將序列對應看成一個完全二叉樹,完全二叉樹中所有非終端節點的值均不大於(或者不小於)其左右孩子節點的值,可以稱作為堆。由堆的性質可以知道堆頂是一個最大關鍵字(或者最小關鍵字)。在輸出堆頂後,使剩下的元素又建成一個堆,然後在輸出對頂。如此反覆執行,便能得到一個有序序列,這個過程成便是堆排序。

堆排序主要分為兩個步驟:

1)從無序序列建堆。(2)輸出對頂元素,在調成一個新堆。

舉例程式設計:int b[10]={77,1,65,13,81,93,10,5,23,17}

//堆排序
void HeapSort(int b[10])
{
void HeapAdjuest(int b[10],int min,int max);
void Sawp(int *a,int *b);
int i;
//因為是完成二叉樹,所以從最後一個非葉子節點開始堆轉換
for(i=9/2;i>=0;i--)
{
HeapAdjuest(b,i,
9);
}
//拿出堆頂資料在從新堆排序
for(i=9;i>0;i--)
{
Sawp(
&b[i],&b[0]);
HeapAdjuest(b,
0,i-1);
}
}

//堆調整(大頂堆)
//min 資料需要調整在陣列中的開始位置
//max 資料需要調整在資料中的結束位置
void HeapAdjuest(int b[10],int min,int max)
{
if(max<=min)return ;
int temp;
temp
=b[min];
int j;

//延它的孩子節點迴圈
for(j=2*min;j<=max;j*=2)
{
//選擇它的大孩子
if(j<max&&b[j]<b[j+1])
{
j
++;
}

//堆頂大於它的孩子不做處理
if(temp>b[j])
{
break;
}

//將大的數替換成小的數
b[min]=b[j];
min
=j;
}
b[min]
=temp;
}

//交換函式
void Sawp(int *a,int *b)
{
int temp;
temp
=*a;
*a=*b;
*b=temp;
}

效能分析:時間複雜度時間複雜度O(nlogn)

 

歸併演算法

又稱2路歸併演算法

使用條件:可對比大小的集合。

演算法思想:假設初始序列含有n個記錄,則可看成n個有序的子序列,每個子序列長度為1,然後兩兩歸併,得到[n/2]個長度為2或者為1(這裡長度為1可能這裡序列長度是奇數,那麼最後一個序列就落單了,所以長度為1);在兩兩歸併,如此重複,直至得到一個長度為n的有序序列為止。

舉例程式設計:int b[10]={77,1,65,13,81,93,10,5,23,17}

//歸併排序
void MergeSort(int b[10],int d[10],int min,int max)
{
//用與存放中間分割槽域得到的序列
int c[10];
void Merge(int c[10],int d[10],int min,int mid,int max);
if(min==max)d[min]=b[min];
else
{
//平分成兩個區域
int mid=(min+max)/2;
//將這個區域進行歸併排序
MergeSort(b,c,min,mid);
//將這個區域進行歸併排序
MergeSort(b,c,mid+1,max);
//兩個區域歸併
Merge(c,d,min,mid,max);
}
}

//將有序序列d[min-mid]與d[mid+1-max]歸併成有序序列c[min-max]
void Merge(int c[10],int d[10],int min,int mid,int max)
{
int i,j,k;
for(i=j=min,k=mid+1;j<=mid&&k<=max;i++)
{
if(c[j]>c[k])
{
d[i]
=c[k];
k
++;
}
else
{
d[i]
=c[j];
j
++;
}
}
if(j<=mid)
{
for(;j<=mid;j++,i++)
{
d[i]
=c[j];
}
}
if(k<=max)
{
for(;k<=max;k++,i++)
{
d[i]
=c[k];
}
}
}

效能分析:時間複雜度O(nlogn)

 

總結

那麼這麼多排序演算法,到底什麼時候用什麼樣的演算法呢?

因為不同的排序方法適應不同的應用換進和要求,選擇合適的排序方法考慮以下因素:

  • 待排序的記錄數n
  • 對其穩定性要求
  • 儲存結構
  • 時間和輔助空間複雜度

(1)如果n比較小(例如n<=50),可採用直接插入排序或者簡單選擇排序。

(2)如果序列初始狀態基本有序,則可選用直接插入排序,氣泡排序。

(3)如果n比價大,則可採用時間複雜度為O(nlogn)的演算法:快速排序,堆排序,歸併排序。

  快速排序被認為目前基於比較的內部排序中最好的方法。當帶排序的關鍵字隨機分佈時,快速排序平均時間最短。 不穩定

  堆排序所需要的輔助空間小於快速排序,並且不會出現快速排序可能出現的最壞情況。 但還是比較不穩定

  歸併排序,比較穩定,但是歸併排序一般不提倡使用,實用性很差,佔用的輔助空間肯能個比較大。

 

相關文章