數據結構中各類排序算法

數據結構中各類排序算法

一、插入排序

1.直接插入排序

 直接插入排序(straight insertion sort)的做法是:
 每次從無序表中取出第一個元素,把它插入到有序表的合適位置,使有序表仍然有序。
 第一趟比較前兩個數,然後把第二個數按大小插入到有序表中; 第二趟把第三個數據與前兩個數從前向後掃描,把第三個數按大小插入到有序表中;依次進行下去,進行了(n-1)趟掃描以後就完成了整個排序過程。
 直接插入排序屬於穩定的排序,最壞時間複雜性爲O(n^2),空間複雜度爲O(1)。
 直接插入排序是由兩層嵌套循環組成的。外層循環標識並決定待比較的數值。內層循環爲待比較數值確定其最終位置。直接插入排序是將待比較的數值與它的前一個數值進行比較,所以外層循環是從第二個數值開始的。當前一數值比待比較數值大的情況下繼續循環比較,直到找到比待比較數值小的並將待比較數值置入其後一位置,結束該次循環。
 C++代碼:
#include<iostream>
usingnamespacestd;
int main()
{
    int a[]={98,76,109,34,67,190,80,12,14,89,1};
    int k=sizeof(a)/sizeof(a[0]);
    int j;
    for(int i=1;i<k;i++)//循環從第2個元素開始
    {
        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;//此處就是a[j+1]=temp;
        }
    }
    for(intf=0;f<k;f++)
    {
        cout<<a[f]<<"";
    }
    return0;
}
      直接插入排序的優缺點:
優點:算法簡單、易行。當待排序記錄數量較少時,該算法非常有效。
缺點:期望複雜度爲O(n^2)。

     2.Shell排序(希爾排序)

又名縮小增量排序。
基本思想:先取一個小於n的整數d1作爲第一個增量,把文件的全部記錄分成d1個組。所有距離爲dl的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然後,取第二個增量d2<d1重複上述的分組和排序,直至所取的增量dt=1(dt<dt-l<;…<d2<d1),即所有記錄放在同一組中進行直接插入排序爲止。該方法實質上是一種分組插入方法。
希爾排序示意圖:

Shell排序的執行時間依賴於增量序列。
好的增量序列的共同特徵:
① 最後一個增量必須爲1;
② 應該儘量避免序列中的值(尤其是相鄰的值)互爲倍數的情況。
希爾排序的優缺點:
優點:對直接插入法的一種改進,屬於分組插入法,如果增量的選取比較合理,時間複雜度爲O(n(logn)^2)。
缺點:是一種不穩定的算法,性能取決於增量的選取。
C語言代碼:
/*
*D.Shell最初的算法。
*/
int shellsortSh(int p[],int n)
{
int op=0;
int h,i,j,temp;
for(h=n/2;h>0;h=h/2){
for(i=h;i<n;i++){
temp=p[i];
for(j=i-h;j>=0&&p[j]>temp;j-=h){
p[j+h]=p[j];
op++;
}
p[j+h]=temp;
op++;
}
}
return op;
}

二、交換排序

1.冒泡排序

它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。
這個算法的名字由來是因爲越大的元素會經由交換慢慢“浮”到數列的頂端,故名。
冒泡排序示意圖:

冒泡排序:時間複雜度爲O(n^2),穩定排序算法。
C語言代碼:
#include<stdio.h>
#defineSIZE8
 
voidbubble_sort(inta[],intn);
 
voidbubble_sort(inta[],intn)//n爲數組a的元素個數
{
    inti,j,temp;
    for(j=0;j<n-1;j++)
        for(i=0;i<n-1-j;i++)
        {
            if(a[i]>a[i+1])//數組元素大小按升序排列
            {
                temp=a[i];
                a[i]=a[i+1];
                a[i+1]=temp;
            }
        }
}
intmain()
{
    intnumber[SIZE]={95,45,15,78,84,51,24,12};
    inti;
    bubble_sort(number,SIZE);
    for(i=0;i<SIZE;i++)
    {
        printf("%d",number[i]);
    }
    printf("\n");
}

對冒泡排序的改進:交替冒泡排序
基本思想:冒泡排序每次冒泡把最大的數上浮到最頂端,而交替冒泡排序爲將最大數上浮與最小數下沉交替進行的,事實證明,這樣更具有效率。

     2.快速排序

快速排序(Quicksort)是對冒泡排序的一種改進。它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
假設用戶輸入瞭如下數組:
下標
0
1
2
3
4
5
數據
6
2
7
3
8
9
創建變量i=0(指向第一個數據), j=5(指向最後一個數據), k=6(賦值爲第一個數據的值)。
我們取走了下標0的數據,於是,我們需要找到一個數字來替換他。由於我們要把所有比6小的數移動到左面,所以我們可以開始尋找比6小的數並從右往左找。別急,我們要按順序找哦。不斷遞減j的值,我們發現下標3的數據比6小,於是把3移到下標0(實際是i指向的位置。代碼中要用i,因爲後面還會循環這個步驟,不用i的話第二次循環:
下標
0
1
2
3
4
5
數據
3
2
7
3
8
9
i=0 j=3 k=6
由於變量k已經儲存了下標0的數據,所以我們可以放心的把下標0覆蓋了。如此一來,下標3雖然有數據,但是相當於沒有了,因爲數據已經複製到別的地方了。於是我們再找一個數據來替換他。這次要變成找比k大的了,而且要從前往後找了。遞加變量i,發現下標2是第一個比k大的,於是用下標2的數據7替換j指向的下標3的數據,數據狀態變成下表:
下標
0
1
2
3
4
5
數據
3
2
7
7
8
9
i=2 j=3 k=6
重複上面的步驟,遞減變量j。這時,我們發現i和j“碰頭”了:他們都指向了下標2。於是,循環結束,把k填回下標2裏,即得到結果。
填回k之後狀態爲:
下標
0
1
2
3
4
5
數據
3
2
6
7
8
9
如果i和j沒有碰頭的話,就遞加i找大的,還沒有,就再遞減j找小的,如此反覆,不斷循環。注意判斷和尋找是同時進行的。
       注意:快速排序不會直接得到最終結果,只會把比k大和比k小的數分到k的兩邊。(你可以想象一下i和j是兩個機器人,數據就是大小不一的石頭,先取走i前面的石頭留出迴旋的空間,然後他們輪流分別挑選比k大和比k小的石頭扔給對面,最後在他們中間把取走的那塊石頭放回去,於是比這塊石頭大的全扔給了j那一邊,小的全扔給了i那一邊。只是這次運氣好,扔完一次剛好排整齊。)爲了得到最後結果,需要再次對下標2兩邊的數組分別執行此步驟,然後再分解數組,直到數組不能再分解爲止(只有一個數據),才能得到正確結果。
C語言代碼:
void QuickSort(int a[],int numsize)/*a是整形數組,numsize是元素個數*/
{
 int i=0,j=numsize-1;
 int val=a[0];/*指定參考值val大小*/
 if(numsize>1)/*確保數組長度至少爲2,否則無需排序*/
 {
 while(i<j)/*循環結束條件*/
 {
 /*從後向前搜索比val小的元素,找到後填到a[i]中並跳出循環*/
 for(;j>i;j--)
 if(a[j]<val)
 {
 a[i++]=a[j];
 break;
 }
 /*從前往後搜索比val大的元素,找到後填到a[j]中並跳出循環*/
 for(;i<j;i++)
 if(a[i]>val)
 {
 a[j--]=a[i];
 break;
 }
 }
 a[i]=val;/*將保存在val中的數放到a[i]中*/
 QuickSort(a,i);/*遞歸,對前i個數排序*/
 QuickSort(a+i+1,numsize-i-1);/*對i+2到numsize這numsize-1-i個數排序*/
 }
}

算法時間複雜度:O(nlogn)

三、選擇排序

1.直接選擇排序
直接選擇排序(Straight Select Sorting) 也是一種簡單的排序方法,它的基本思想是:第一次從R[0]~R[n-1]中選取最小值,與R[0]交換,第二次從R[1]~R[n-1]中選取最小值,與R[1]交換,....,第i次從R[i-1]~R[n-1]中選取最小值,與R[i-1]交換,.....,第n-1次從R[n-2]~R[n-1]中選取最小值,與R[n-2]交換,總共通過n-1次,得到一個按排序碼從小到大排列的有序序列·
例如:
給定n=8,數組R中的8個元素的排序碼爲(8,3,2,1,7,4,6,5),則直接選擇排序的過程如下所示:
初始狀態 [ 8 3 2 1 7 4 6 5 ] 8 -- 1
第一次 [ 1 3 2 8 7 4 6 5 ] 3 -- 2
第二次 [ 1 2 3 8 7 4 6 5 ] 3 -- 3
第三次 [ 1 2 3 8 7 4 6 5 ] 8 -- 4
第四次 [ 1 2 3 4 7 8 6 5 ] 7 -- 5
第五次 [ 1 2 3 4 5 8 6 7 ] 8 -- 6
第六次 [ 1 2 3 4 5 6 8 7 ] 8 -- 7
第七次 [ 1 2 3 4 5 6 7 8 ] 
排序完成。
算法的時間複雜度:O(n^2)

2.堆排序

堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。可以利用數組的特點快速定位指定索引的元素。堆分爲大根堆和小根堆,是完全二叉樹。大根堆的要求是每個節點的值都不大於其父節點的值,即A[PARENT[i]] >= A[i]。在數組的非降序排序中,需要使用的就是大根堆,因爲根據大根堆的要求可知,最大的值一定在堆頂。
算法時間複雜度:O(nlogn),不穩定算法。





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