幾種排序算法的認識

全部排序算法總結:冒泡排序,快速排序,插入排序,歸併排序,堆排序,shell排序,選擇排序html

算法排序的穩定性是指:關鍵碼相同的記錄排序先後相對位置不發生改變。舉個例子:ios

待排序數組:int a[] ={1, 2, 2, 3, 4, 5, 6};
算法

在快速排序的隨機選擇比較子(即pivot)階段:shell

若選擇a[2](即數組中的第二個2)爲比較子,,而把大於等於比較子的數均放置在大數數組中,則a[1](即數組中的第一個2)會到pivot的右邊, 那麼數組中的兩個2非原序(這就是「不穩定」)。windows

若選擇a[1]爲比較子,而把小於等於比較子的數均放置在小數數組中,則數組中的兩個2順序也非原序。數組

1. 冒泡排序函數

  很簡單的排序,外層循環是n-1趟,內層循環是n-1次兩兩比較。主要思路:從底部往上冒泡,經過無序區中相鄰記錄關鍵字間的比較和位置的交換,使關鍵字最小的記錄如氣泡通常逐漸往上「漂浮」直至「水面」。測試

就是在每一趟內層循環完畢以後,最小的那個值會像氣泡同樣上浮到第一個位置(從小到大排序),這樣循環執行n-1趟,每一趟都是從最後一個值開始進行兩兩比較,把每趟中的最小的值往上浮。(注意內層循環的終止條件是j>i,由於i以前是已經放置好的有序的最小值ui

代碼:spa

static void bubblesort(int* A,int n){
    if(A==NULL) return;
    for (int i=0;i<n-1;++i)
    {
        for (int j=n-1;j>i;--j)
        {
            if(A[j]<A[j-1]){int temp=A[j];A[j]=A[j-1];A[j-1]=temp;}
        }
        cout<<""<<i+1<<"趟冒泡排序:"<<endl;
        for (int i=0;i<n;++i)
        {
            cout<<A[i]<<" ";
        }
        cout<<endl;
    }
}

 

2. 歸併排序(穩定,效率高,採用遞歸)

  思路:基本思路就是將數組分紅二組A,B,若是這二組組內的數據都是有序的,那麼就能夠很方便的將這二組數據進行排序。問題是如何讓這二組組內數據有序呢?

  能夠將A,B組各自再分紅二組。依次類推,當分出來的小組只有一個數據時,能夠認爲這個小組組內已經達到了有序,而後再合併相鄰的二個小組就能夠了。這樣經過先遞歸的分解數列,再合併數列就完成了歸併排序。具體過程參考下圖:

代碼(先用遞歸分解數組,而後用mergearray合併):

//將有二個有序數列a[first...mid]和a[mid...last]合併。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
    int i = first, j = mid + 1;//i爲分出的第一個數組的第一個位置,j爲分出來的第二個數組的第一個位置
    int m = mid,n = last;//m爲分出的第一個數組的末尾,n爲分出來的第二個數組的末尾
    int k = 0;

    while (i <= m && j <= n)
    {
        if (a[i] <= a[j])
            temp[k++] = a[i++];
        else
            temp[k++] = a[j++];
    }

    while (i <= m)
        temp[k++] = a[i++];

    while (j <= n)
        temp[k++] = a[j++];

    for (i = 0; i < k; i++)
        a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
    if (first < last)
    {
        int mid = (first + last) / 2;
        mergesort(a, first, mid, temp);    //左邊有序
        mergesort(a, mid + 1, last, temp); //右邊有序
        mergearray(a, first, mid, last, temp); //再將二個有序數列合併
    }
}

bool MergeSort(int a[], int n)
{
    int *p = new int[n];
    if (p == NULL)
        return false;
    mergesort(a, 0, n - 1, p);
    delete[] p;
    return true;
}

 

1.堆

  堆其實是一棵徹底二叉樹,其任何一非葉節點知足性質:

  Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]

  即任何一非葉節點的關鍵字不大於或者不小於其左右孩子節點的關鍵字。

  堆分爲大頂堆和小頂堆,知足Key[i]>=Key[2i+1]&&key>=key[2i+2]稱爲大頂堆,知足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]稱爲小頂堆。由上述性質可知大頂堆的堆頂的關鍵 字確定是全部關鍵字中最大的,小頂堆的堆頂的關鍵字是全部關鍵字中最小的。

2.堆排序的思想

   利用大頂堆(小頂堆)堆頂記錄的是最大關鍵字(最小關鍵字)這一特性,使得每次從無序中選擇最大記錄(最小記錄)變得簡單。

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

    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]同該區間的最後一個記錄交換,而後將新的無序區調整爲新的堆。

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

    下面舉例說明:

     給定一個整形數組a[]={16,7,3,20,17,8},對其進行堆排序。

    首先根據該數組元素構建一個徹底二叉樹,獲得

 
 而後須要構造初始堆,則從最後一個非葉節點開始調整,調整過程以下:

20和16交換後致使16不知足堆的性質,所以需從新調整

這樣就獲得了初始堆。

即每次調整都是從父節點、左孩子節點、右孩子節點三者中選擇最大者跟父節點進行交換(交換以後可能形成被交換的孩子節點不知足堆的性質,所以每次交換以後要從新對被交換的孩子節點進行調整)。有了初始堆以後就能夠進行排序了。

此時3位於堆頂不滿堆的性質,則需調整繼續調整

 這樣整個區間便已經有序了。
    從上述過程可知,堆排序其實也是一種選擇排序,是一種樹形選擇排序。只不過直接選擇排序中,爲了從R[1...n]中選擇最大記錄,需比較n-1次,而後 從R[1...n-2]中選擇最大記錄需比較n-2次。事實上這n-2次比較中有不少已經在前面的n-1次比較中已經作過,而樹形選擇排序剛好利用樹形的 特色保存了部分前面的比較結果,所以能夠減小比較次數。對於n個關鍵字序列,最壞狀況下每一個節點需比較log2(n)次,所以其最壞狀況下時間複雜度爲 nlogn。堆排序爲不穩定排序,不適合記錄較少的排序。
 
測試程序
複製代碼
/*堆排序(大頂堆) 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;
}
複製代碼
 
4.快速排序
   思路:主要patition函數的編寫,即先選一個值做爲哨兵,而後把小於該哨兵的數字放在其左邊(後稱左邊序列),而後把大於哨兵的數字放在右邊(後稱右邊序列)。該函數有幾種實現方法,請參見http://www.cnblogs.com/mfryf/archive/2012/08/06/2625300.html
  該函數用small指針來表示左邊序列的最後一個位置,當發現某個值A[i]小於哨兵時,只需先對該small指針後移一位(移到右邊序列的第一位),而後將A[small]和A[i]互換便可,換完以後,small指針仍然指向左邊序列最後一個位置。
  因此說,數組A,在進行一輪快速排序的時候,實際上有一個哨兵和三個序列,左邊序列,右邊序列,待排序列。
 
具體如圖所示:
 
 
5.直接插入排序(最好O(n),最壞O(n2))參考博文
  思路:把一個序列分爲兩個區,前部分是已排好的序列sorted,然後部分則是待排序列wait_sort,插入排序就是選擇wait_sort中的第一個元素,直接插入到sorted序列中。
  假設要插入元素a[i],則插入的步驟分爲3步:1>在sorted序列中找到要插入元素的位置j。2>將位置j以後的序列日後移動一位,給帶插入的a[i]空出一個位置。3>把元素a[i]插入到sorted序列中。
 

設數組爲a[0…n-1]。

1.      初始時,a[0]自成1個有序區,無序區爲a[1..n-1]。令i=1

2.      將a[i]併入當前的有序區a[0…i-1]中造成a[0…i]的有序區間。

3.      i++並重復第二步直到i==n-1。排序完成。

 

下面給出嚴格按照定義書寫的代碼(由小到大排序):

void Insertsort1(int a[], int n)
{
    int i, j, k;
    for (i = 1; i < n; i++)
    {
        //爲a[i]在前面的a[0...i-1]有序區間中找一個合適的位置
        for (j = i - 1; j >= 0; j--)
            if (a[j] < a[i])
                break;

        //如找到了一個合適的位置
        if (j != i - 1)
        {
            //將比a[i]大的數據向後移
            int temp = a[i];
            for (k = i - 1; k > j; k--)
                a[k + 1] = a[k];
            //將a[i]放到正確位置上
            a[j+1] = temp;
        }
    }
}

  

  這樣的代碼太長了,不夠清晰。如今進行一下改寫,將搜索和數據後移這二個步驟合併。即每次a[i]先和前面一個數據a[i-1]比較,若是a[i] > a[i-1]說明a[0…i]也是有序的,無須調整。不然就令j=i-1,temp=a[i]。而後一邊將數據a[j]向後移動一邊向前搜索,當有數據a[j]<a[i]時中止並將temp放到a[j + 1]處。

void Insertsort2(int a[], int n)
{
    int i, j;
    for (i = 1; i < n; i++)
        if (a[i] < a[i - 1])
        {
            int temp = a[i];
            for (j = i - 1; j >= 0 && a[j] > temp; j--)
                a[j + 1] = a[j];
            a[j + 1] = temp;
        }
}
相關文章
相關標籤/搜索