數據結構排序

在待排序的文件中,若存在多個關鍵字相同的記錄,經過排序後這些具有相同關鍵字的記錄之間的相對次序保持不變,該排序方法是穩定的;若具有相同關鍵字的記錄之間的相對次序發生改變,則稱這種排序方法是不穩定的。即所有相等的數經過某種排序方法後,仍能保持它們在排序之前的相對次序,則說這種排序算法是穩定的,反之,就是不穩定的。

   穩定的排序算法如下表所示:   

穩定的排序

時間複雜度

空間複雜度

冒泡排序(bubble sort)

最差、平均都是O(n^2),最好是O(n)

1

插入排序(insertion sort)

最差、平均都是O(n^2),最好是O(n)

1

歸併排序(merge sort)

最差、平均、最好都是O(n log n)

O(n)

桶排序(bucket sort)

O(n)

O(k)

基數排序(Radix sort)

O(dn)d是常數)

O(n)

二叉樹排序(Binary tree sort)

O(n log n)

O(n)

   

   不穩定的排序算法如下表所示: 

不穩定的排序

時間複雜度

空間複雜度

選擇排序(selection sort)

最差、平均都是O(n^2)

1

希爾排序(shell sort)

O(n log n)

1

堆排序(heapsort)

最差、平均、最好都是O(n log n)

1

快速排序(quicksort)

平均是O(n log n),最差是O(n^2)

O(log n)

 

一、冒泡排序

   冒泡排序(BubbleSort)的基本概念是:依次比較相鄰的兩個數,將小數放在前面,大數放在後面。即在第一趟:首先比較第1個和第2個數,將小數放前,大數放後。然後比較第2個數和第3個數,將小數放前,大數放後,如此繼續,直至比較最後兩個數,將小數放前,大數放後。至此第一趟結束,將最大的數放到了最後。在第二趟:仍從第一對數開始比較(因爲可能由於第2個數和第3個數的交換,使得第1個數不再小於第2個數),將小數放前,大數放後,一直比較到倒數第二個數(倒數第一的位置上已經是最大的),第二趟結束,在倒數第二的位置上得到一個新的最大數(其實在整個數列中是第二大的數)。如此下去,重複以上過程,直至最終完成排序。

   代碼實現如下:

【數據結構】排序算法總結

 

二、插入排序   

   插入排序的基本思想是每步將一個待排序的記錄按其排序碼值的大小,插到前面已經排好的文件中的適當位置,直到全部插入完爲止。插入排序方法主要有直接插入排序和希爾排序。

   

   直接插入排序具體算法描述如下:

   1.從第一個元素開始,該元素可以認爲已經被排序

  2. 取出下一個元素,在已經排序的元素序列中從後向前掃描

  3. 如果該元素(已排序)大於新元素,將該元素移到下一位置

  4. 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置

  5. 將新元素插入到下一位置中

  6. 重複步驟2

   

   僞碼描述如下:

【數據結構】排序算法總結
   代碼實現如下:
【數據結構】排序算法總結

三、歸併排序

   歸併排序是將兩個或兩個以上的有序子表合併成一個新的有序表。初始時,把含有n個結點的待排序序列看作由n個長度都爲1的有序子表組成,將它們依次兩兩歸併得到長度爲2的若干有序子表,再對它們兩兩合併。直到得到長度爲n的有序表,排序結束。
   歸併操作的工作原理如下:

  1、申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列

  2、設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置

  3、比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置

  4、重複步驟3直到某一指針達到序列尾

  5、將另一序列剩下的所有元素直接複製到合併序列尾

   代碼實現如下:

【數據結構】排序算法總結

【數據結構】排序算法總結

四、桶排序
   桶排序的基本思想就是把區間[0,1)劃分成n個相同大小的子區間,或稱桶,然後將n個輸入數分佈到各個桶中去。因爲輸入數均勻分佈在[0,1)上,所以一般不會有很多數落在一個桶中的情況。爲得到結果,先對各個桶中的數進行排序,然後按次序把各桶中的元素列出來即可。

  在桶排序算法的代碼中,假設輸入是個含n個元素的數組A,且每個元素滿足0≤A[i]<1。另外還需要一個輔助數組B[O..n-1]來存放鏈表實現的桶,並假設可以用某種機制來維護這些表。 


   我的理解是:桶排序相當於一個N路的歸併排序,首先將輸入按均勻分佈分到N個桶中,每一個桶都用一個鏈表來維護,並用插入排序對每個桶(也就是每一路)進行排序,最後將N個有序桶合併成一個,即得最終的排序結果。 

   僞碼實現如下:

【數據結構】排序算法總結

五、基數排序

    設單關鍵字的每個分量的取值範圍均是C0<=Kj<=Crd-1(0<=j<=rd),可能的取值個數rd稱爲基數.基數的選擇和關鍵字的分解因關鍵字的類型而異.
  (1)若關鍵字是十進制整數,則按個、十等位進行分解,基數rd=10,C0=0,C9=9,d爲最長整數的位數.
  (2)若關鍵字是小寫的英文字符串,則rd=26,C0='a',C25='z',d爲最長字符串的長度.
  基數排序的基本思想是:從低位到高位依次對待排序的關鍵碼進行分配和收集,經過d趟分配和收集,就可以得到一個有序序列.
   

   基數排序從低位到高位進行,使得最後一次計數排序完成後,數組有序。其原理在於對於待排序的數據,整體權重未知的情況下,先按權重小的因子排序,然後按權重大的因子排序。例如比較時間,先按日排序,再按月排序,最後按年排序,僅需排序三次。但是如果先排序高位就沒這麼簡單了。基數排序源於老式穿孔機,排序器每次只能看到一個列,很多教科書上的基數排序都是對數值排序,數值的大小是已知的,與老式穿孔機不同。將數值按位拆分再排序,是無聊並自找麻煩的事。算法的目的是找到最佳解決問題的方案,而不是把簡單的事搞的更復雜。基數排序更適合用於對時間、字符串等這些整體權值未知的數據進行排序。

 

   我的理解是:基數排序算法中,數據可分解爲d個因子,每個因子對排序結果都有影響(即權重),先按權重小的因子進行排序,後按權重大的因子進行排序,所有因子排序完即得結果。如時間可分解爲三個因子:日、月、年,先按日對時間排序,再按月對時間排序,最後按年對時間進行排序,即可。注意:每一趟按因子進行的排序都必須是穩定的!

   僞碼實現如下:

【數據結構】排序算法總結

 

六、二叉樹排序

   二叉排序樹(Binary Sort Tree)又稱二叉查找樹。它或者是一棵空樹;或者是具有下列性質的二叉樹:

   (1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;

   (2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;

   (3)左、右子樹也分別爲二叉排序樹;

 

   我的理解是:二叉樹排序,即先建一個二叉排序樹,然後中序遍歷,即得到一個從小到大的排序結果。

 

   插入結點:

   1、首先執行查找算法,找出被插結點的父親結點。

  2、判斷被插結點是其父親結點的左、右兒子。將被插結點作爲葉子結點插入。

  3、若二叉樹爲空。則首先單獨生成根結點。

  PS:新插入的結點總是葉子結點。依次插入數據即完成建樹。

   僞碼實現如下:

【數據結構】排序算法總結

   刪除結點:

   將結點z從二叉排序樹中刪除,分三種情況討論:

  1、如果結點z沒有子女,則修改其父結點p[z],使NIL爲其子女;

   2、如果結點z只有一個子女,則可以通過在其子結點與父結點間建立一條鏈來刪除z;

   3、如果結點z只有兩個子女,先刪除z的後繼y(它沒有左子女),再用y的內容來替代z的內容。

   PS:某一結點x的後繼即具有大於key[x]中的關鍵字中最小者的那個結點,即中序遍歷順序下的後繼

   僞碼實現如下:

【數據結構】排序算法總結

七、選擇排序

   選擇排序的基本思想是每一趟從待排序的數據元素中選出最小(或最大)的一個元素,順序放在已排好序的數列的最後,直到全部待排序的數據元素排完。選擇排序中主要使用直接選擇排序和堆排序。
   

   直接選擇排序的過程是:首先在所有記錄中選出序碼最小的記錄,把它與第1個記錄交換,然後在其餘的記錄內選出排序碼最小的記錄,與第2個記錄交換......依次類推,直到所有記錄排完爲止。

   代碼實現如下:

【數據結構】排序算法總結

八、希爾排序

     希爾(Shell)排序的基本思想是:先取一個小於n的整數d1作爲第一個增量把文件的全部記錄分成d1個組。所有距離爲d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然後,取得第二個增量d2<d1重複上述的分組和排序,直至所取的增量di=1,即所有記錄放在同一組中進行直接插入排序爲止。該方法實質上是一種分組插入方法。
    一般取d1=n/2,di+1=di/2。如果結果爲偶數,則加1,保證di爲奇數。
   代碼實現如下:

   【數據結構】排序算法總結

 

九、堆排序

   堆的定義:n個關鍵字序列Kl,K2,…,Kn稱爲(Heap),當且僅當該序列滿足如下性質(簡稱爲堆性質):

  (1) ki≤K2i 且 ki≤K2i+1

   或(2)Ki≥K2i 且 ki≥K2i+1(1≤i≤ n)

   若將此序列所存儲的向量R[1..n]看作是一棵完全二叉樹的存儲結構,則堆實質上是滿足如下性質的完全二叉樹:樹中任一非葉結點的關鍵字均不大於(或不小於)其左右孩子(若存在)結點的關鍵字。
   根結點(堆頂)的關鍵字是堆裏所有結點關鍵字中最小者,稱爲小根堆;根結點的關鍵字是堆裏所有結點關鍵字中最大者,稱爲大根堆。
   

   用大根堆排序的基本思想如下:

  1、先將初始文件R[1..n]建成一個大根堆,此堆爲初始的無序區

  2、再將關鍵字最大的記錄R[1](即堆頂)和無序區的最後一個記錄R[n]交換,由此得到新的無序區R[1..n-1]和有序區R[n],且滿足R[1..n-1].keys≤R[n].key

  3、由於交換後新的根R[1]可能違反堆性質,故應將當前無序區R[1..n-1]調整爲堆。然後再次將R[1..n-1]中關鍵字最大的記錄R[1]和該區間的最後一個記錄R[n-1]交換,由此得到新的無序區R[1..n-2]和有序區R[n-1..n],且仍滿足關係R[1..n-2].keys≤R[n-1..n].keys,同樣要將R[1..n-2]調整爲堆。

  ……

  直到無序區只有一個元素爲止。

  僞碼實現如下:

【數據結構】排序算法總結

【數據結構】排序算法總結

【數據結構】排序算法總結

十、快速排序
   快速排序採用了一種分治的策略,通常稱其爲分治法,其基本思想是:將原問題分解爲若干個規模更小但結構與原問題相似的子問題。遞歸地解這些子問題,然後將這些子問題的解組合爲原問題的解。
    快速排序的具體過程如下:
    第一步,在待排序的n個記錄中任取一個記錄,以該記錄的排序碼爲準,將所有記錄分成兩組,第1組各記錄的排序碼都小於等於該排序碼,第2組各記錄的排序碼都大於該排序碼,並把該記錄排在這兩組中間。
    第二步,採用同樣的方法,對左邊的組和右邊的組進行排序,直到所有記錄都排到相應的位置爲止。

 

   代碼實現如下:

【數據結構】排序算法總結 

 


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