排序算法

 1.二路歸併排序算法

   將兩個按值有序序列合併成一個按值有序序列,則稱之爲二路歸併排序,下面有自底向上和自頂向下的兩種排序算法。
   二路歸併排序算法:將參加排序的初始序列分成長度爲1的子序列使用mergePass函數進行第一趟排序,得到 n / 2 個長度爲 2 的各自有序的子序列(若n爲奇數,還會存在一個最後元素的子序列),再一次調用mergePass函數進行第二趟排序,得到 n / 4 個長度爲 4 的各自有序的子序列, 第 i 趟排序就是兩兩歸併長度爲 2^(i-1) 的子序列得到 n / (2^i) 長度爲 2^i 的子序列,直到最後只剩一個長度爲n的子序列。由此看出,一共需要 log2n 趟排序,每一趟排序的時間複雜度是 O(n), 由此可知
該算法的總的時間複雜度是是 O(n log2n),但是該算法需要 O(n) 的輔助空間,空間複雜度很大,是 O(n).
看代碼:

  1. void mergeSort(int X[], int n)  
  2. {  
  3.     int t = 1;  
  4.     int *Y = (int *)malloc(sizeof(int) * n);  
  5.     while( t < n )  
  6.     {  
  7.         mergePass(X, Y, n, t);  
  8.         t *= 2;  
  9.         mergePass(Y, X, n, t);  
  10.         t *= 2;  
  11.     }  
  12.     free(Y);  
  13. }  

2.快速排序

   快速排序是找出一個元素(理論上可以隨便找一個)作爲基準(pivot),然後對數組進行分區操作,使基準左邊元素的值都不大於基準值,基準右邊的元素值 都不小於基準值,如此作爲基準的元素調整到排序後的正確位置。遞歸快速排序,將其他n-1個元素也調整到排序後的正確位置。最後每個元素都是在排序後的正確位置,排序完成。所以快速排序算法的核心算法是分區操作,即如何調整基準的位置以及調整返回基準的最終位置以便分治遞歸。

<span style="font-family:KaiTi_GB2312;">#include<stdio.h>
void quickSort(int a[],int left,int right)
{
  int i=left;
  int j=right;
  int temp=a[left];
  if(left>=right)
    return;
  while(i!=j)
  {
    while(i<j&&a[j]>=temp) 
    j--;
    if(j>i)
      a[i]=a[j];//a[i]已經賦值給temp,所以直接將a[j]賦值給a[i],賦值完之後a[j],有空位
    while(i<j&&a[i]<=temp)
    i++;
    if(i<j)
      a[j]=a[i];
  }
  a[i]=temp;//把基準插入,此時i與j已經相等R[low..pivotpos-1].keys≤R[pivotpos].key≤R[pivotpos+1..high].keys
  quickSort(a,left,i-1);/*遞歸左邊*/
  quickSort(a,i+1,right);/*遞歸右邊*/
}

int main()
{
int a[9]={8,2,6,12,1,9,5,5,10};
int i;
quickSort(a,0,8);/*排好序的結果*/
for(i=0;i<8;i++)
printf("%4d",a[i]);
getchar();
return 0;
}</span>

快速排序的時間主要耗費在劃分操作上,對長度爲k的區間進行劃分,共需k-1次關鍵字的比較。

最壞情況是每次劃分選取的基準都是當前無序區中關鍵字最小(或最大)的記錄,劃分的結果是基準左邊的子區間爲空(或右邊的子區間爲空),而劃分所得的另一個非空的子區間中記錄數目,僅僅比劃分前的無序區中記錄個數減少一個。時間複雜度爲O(n*n)

在最好情況下,每次劃分所取的基準都是當前無序區的"中值"記錄,劃分的結果是基準的左、右兩個無序子區間的長度大致相等。總的關鍵字比較次數:O(nlgn)

儘管快速排序的最壞時間爲O(n2),但就平均性能而言,它是基於關鍵字比較的內部排序算法中速度最快者,快速排序亦因此而得名。它的平均時間複雜度爲O(nlgn)。

3.shell排序
希爾(Shell)排序又稱爲縮小增量排序,它是一種插入排序。它是直接插入排序算法的一種威力加強版。

希爾排序的基本思想是:

把記錄按步長 gap 分組,對每組記錄採用直接插入排序方法進行排序。
隨着步長逐漸減小,所分成的組包含的記錄越來越多,當步長的值減小到 1 時,整個數據合成爲一組,構成一組有序記錄,則完成排序。

在第一趟排序中,我們不妨設 gap1 = N / 2 = 5,即相隔距離爲 5 的元素組成一組,可以分爲 5 組。
接下來,按照直接插入排序的方法對每個組進行排序。
在第二趟排序中,我們把上次的 gap 縮小一半,即 gap2 = gap1 / 2 = 2 (取整數)。這樣每相隔距離爲 2 的元素組成一組,可以分爲 2 組。
按照直接插入排序的方法對每個組進行排序。
在第三趟排序中,再次把 gap 縮小一半,即gap3 = gap2 / 2 = 1。 這樣相隔距離爲 1 的元素組成一組,即只有一組。
按照直接插入排序的方法對每個組進行排序。此時,排序已經結束。
需要注意一下的是,圖中有兩個相等數值的元素 5 和 5 。我們可以清楚的看到,在排序過程中,兩個元素位置交換了。
所以,希爾排序是不穩定的算法。

<span style="font-family:KaiTi_GB2312;">public void shellSort(int[] list) {
    int gap = list.length / 2;
    while (1 <= gap) {      // 把距離爲 gap 的元素編爲一個組,掃描所有組
        for (int i = gap; i < list.length; i++) {
            int j = 0;
            int temp = list[i];            // 對距離爲 gap 的元素組進行排序
            for (j = i - gap; j >= 0 && temp < list[j]; j = j - gap) {
                list[j + gap] = list[j];
            }
            list[j + gap] = temp;
        }
        System.out.format("gap = %d:\t", gap);
        printAll(list);
        gap = gap / 2; // 減小增量
    }
}</span>

步長序列

最壞情況下複雜度

4.冒泡排序
冒泡排序中,最重要的思想是兩兩比較,將兩者較少的升上去
冒泡排序最壞情況的時間複雜度是O(n²)
  • 首先,把10個數裏最小的個數放到下標爲0的位置上(str[0])
  • 通過將下標爲0的數(str[0])與剩下其餘9個數進行對比交換(將較少者放置在下標爲0的位置上),就可以得到這10個數最小的那個
  • 10個數最小的那位確定後,接下來就要找剩下9個數最小的那個。
  • 因爲已經確定出一個最小的數,所以就不要動str[0],直接從str[1]開始,與剩下的8個數對比交換,找出9個數中最小的那位放到下標爲1(str[1])的位置上
  • 繼續按照這個思路就可以將這十個數變成有序的(從小到大)的數組
<span style="font-family:KaiTi_GB2312;">#include <stdio.h>  
void swap(int *a, int *b);  
int main()  
{  
    int    array[10] = {15, 225, 34, 42, 52, 6, 7856, 865, 954, 10};  
    int    i, j;  
    for (i = 0; i < 10; i++)  
    {  
        //每一次由底至上地上升  
        for (j = 9; j > i; j--)  
        {  
            if (array[j] < array[j-1])  
            {  
                    swap(&array[j], &array[j-1]);  
            }  
        }  
    }  
    for (i = 0; i < 10; i++)  
    {  
        printf("%d\n", array[i]);  
    }  
    return    0;  
}  
void swap(int *a, int *b)  
{  
    int    temp;  
    temp = *a;  
      *a = *b;  
      *b = temp;  
}  </span>
5.堆排序

堆實際上是一棵完全二叉樹,其任何一非葉節點滿足性質:

即任何一非葉節點的關鍵字不大於或者不小於其左右孩子節點的關鍵字。由上述性質可知大頂堆的堆頂的關鍵字肯定是所有關鍵字中最大的,小頂堆的堆頂的關鍵字是所有關鍵字中最小的。

其基本思想爲(大頂堆):

    1)將初始待排序關鍵字序列(R1,R2....Rn)構建成大頂堆,此堆爲初始的無序區;

    2)將堆頂元素R[1]與最後一個元素R[n]交換,此時得到新的無序區(R1,R2,......Rn-1)和新的有序區(Rn),且滿足R[1,2...n-1]<=R[n]; 

    3)由於交換後新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,......Rn-1)調整爲新堆,然後再次將R[1]與無序區最後一個元素交換,得到新的無序區(R1,R2....Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數爲n-1,則整個排序過程完成。

 操作過程如下:

     1)初始化堆:將R[1..n]構造爲堆;

     2)將當前無序區的堆頂元素R[1]同該區間的最後一個記錄交換,然後將新的無序區調整爲新的堆。

    因此對於堆排序,最重要的兩個操作就是構造初始堆和調整堆,其實構造初始堆事實上也是調整堆的過程,只不過構造初始堆是對所有的非葉節點都進行調整。

<span style="font-size:14px;"><span style="font-family:KaiTi_GB2312;">/*堆排序(大頂堆) 2011.9.14*/ 
#include <iostream>
#include<algorithm>
using namespace std;
void HeapAdjust(int *a,int i,int size)  //調整堆 
{
    int lchild=2*i;       //i的左孩子節點序號 
    int rchild=2*i+1;     //i的右孩子節點序號 
    int max=i;            //臨時變量 
    if(i<=size/2)          //如果i是葉節點就不用進行調整 
    {
        if(lchild<=size&&a[lchild]>a[max])
        {
            max=lchild;
        }    
        if(rchild<=size&&a[rchild]>a[max])
        {
            max=rchild;
        }
        if(max!=i)
        {
            swap(a[i],a[max]);
            HeapAdjust(a,max,size);    //避免調整之後以max爲父節點的子樹不是堆 
        }
    }        
}

void BuildHeap(int *a,int size)    //建立堆 
{
    int i;
    for(i=size/2;i>=1;i--)    //非葉節點最大序號值爲size/2 
    {
        HeapAdjust(a,i,size);    
    }    
} 

void HeapSort(int *a,int size)    //堆排序 
{
    int i;
    BuildHeap(a,size);
    for(i=size;i>=1;i--)
    {
        //cout<<a[1]<<" ";
        swap(a[1],a[i]);           //交換堆頂和最後一個元素,即每次將剩餘元素中的最大者放到最後面 
          //BuildHeap(a,i-1);        //將餘下元素重新建立爲大頂堆 
          HeapAdjust(a,1,i-1);      //重新調整堆頂節點成爲大頂堆
    }
} 

int main(int argc, char *argv[])
{
     //int a[]={0,16,20,3,11,17,8};
    int a[100];
    int size;
    while(scanf("%d",&size)==1&&size>0)
    {
        int i;
        for(i=1;i<=size;i++)
            cin>>a[i];
        HeapSort(a,size);
        for(i=1;i<=size;i++)
            cout<<a[i]<<"";
        cout<<endl;
    }
    return 0;
}</span></span>

6.插入排序

插入即表示將一個新的數據插入到一個有序數組中,並繼續保持有序。例如有一個長度爲N的無序數組,進行N-1次的插入即能完成排序;第一次,數組第1個數認爲是有序的數組,將數組第二個元素插入僅有1個有序的數組中;第二次,數組前兩個元素組成有序的數組,將數組第三個元素插入由兩個元素構成的有序數組中......第N-1次,數組前N-1個元素組成有序的數組,將數組的第N個元素插入由N-1個元素構成的有序數組中,則完成了整個插入排序。

平均時間複雜度:O(n2)

空間複雜度:O(1)  (用於記錄需要插入的數據)

穩定性:穩定

<span style="font-family:KaiTi_GB2312;font-size:12px;">void InsertSort(int* pDataArray, int iDataNum)  
{  
    for (int i = 1; i < iDataNum; i++)    //從第2個數據開始插入  
    {  
        int j = 0;  
        while (j < i && pDataArray[j] <= pDataArray[i])    //尋找插入的位置  
            j++;  
         if (j < i)    //i位置之前,有比pDataArray[i]大的數,則進行挪動和插入  
        {  
            int k = i;  
            int temp = pDataArray[i];  
            while (k > j)    //挪動位置  
            {  
                pDataArray[k] = pDataArray[k-1];  
                k--;  
            }  
            pDataArray[k] = temp;    //插入  
        }  
    }  
}</span>
7.選擇排序
選擇排序是通過每一趟排序過程中從待排序記錄中選擇出關鍵字最小(大)的記錄,將其依次放在數據表的最前或最後端的方法來實現整個數據表的有序排列。

第一趟排序在所有待排序的n個記錄中選出關鍵字最小的記錄,將它與數據表中的第一個記錄交換位置,使關鍵字最小的記錄處於數據表的最前端;第二趟在剩下的n-1個記錄中再選出關鍵字最小的記錄,將其與數據表中的第二個記錄交換位置,使關鍵字次小的記錄處於數據表的第二個位置;重複這樣的操作,依次選出數據表中關鍵字第三小、第四小…的元素,將它們分別換到數據表的第三、第四…個位置上。排序共進行n-1趟,最終可實現數據表的升序排列。

平均時間複雜度:O(n2)

空間複雜度:O(1)  (用於交換和記錄索引)

穩定性:不穩定 (比如序列【5, 5, 3】第一趟就將第一個[5]與[3]交換,導致第一個5挪動到第二個5後面)

<span style="font-size:14px;"><span style="font-family:KaiTi_GB2312;">//交換data1和data2所指向的整形  
void DataSwap(int* data1, int* data2)  
{  
    int temp = *data1;  
    *data1 = *data2;  
    *data2 = temp;  
}  
  
/******************************************************** 
*函數名稱:SelectionSort 
*參數說明:pDataArray 無序數組; 
*          iDataNum爲無序數據個數 
*說明:    選擇排序 
*********************************************************/  
void SelectionSort(int* pDataArray, int iDataNum)  
{  
    for (int i = 0; i < iDataNum - 1; i++)    //從第一個位置開始  
    {  
        int index = i;  
        for (int j = i + 1; j < iDataNum; j++)    //尋找最小的數據索引   
            if (pDataArray[j] < pDataArray[index])  
                index = j;  
  
        if (index != i)    //如果最小數位置變化則交換  
            DataSwap(&pDataArray[index], &pDataArray[i]);  
    }  
} </span>


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