數據結構62:表插入排序算法


 

前面章節中所介紹到的三種插入排序算法,其基本結構都采用數組的形式進行存儲,因而無法避免排序過程中產生的數據移動的問題。如果想要從根本上解決只能改變數據的存儲結構,改用鏈表存儲。

表插入排序,即使用鏈表的存儲結構對數據進行插入排序。在對記錄按照其關鍵字進行排序的過程中,不需要移動記錄的存儲位置,只需要更改結點間指針的指向。

鏈表的存儲結構用代碼表示為:

#define SIZE 100
typedef struct 
{
    int rc;     //記錄項
    int next;    //指針項,由於在數組中,所以只需要記錄下一個結點所在數組位置的下標即可。
}SLNode;

typedef struct 
{
    SLNode r[SIZE];  //存儲記錄的鏈表
    int length;     //記錄當前鏈表長度
}SLinkListType;

 

在使用數組結構表示的鏈表中,設定數組下標為 0 的結點作為鏈表的表頭結點,並令其關鍵字取最大整數。則表插入排序的具體實現過程是:首先將鏈表中數組下標為 1 的結點和表頭結點構成一個循環鏈表,然后將后序的所有結點按照其存儲的關鍵字的大小,依次插入到循環鏈表中。

例如,將無序表{49,38,76,13,27}用表插入排序的方式進行排序,其過程為:

  • 首先使存儲 49 的結點與表頭結點構成一個初始的循環鏈表,完成對鏈表的初始化,如下表所示:
  • 然后將以 38 為關鍵字的記錄插入到循環鏈表中(只需要更改其鏈表的 next 指針即可),插入后的鏈表為:
  • 再將以 76 為關鍵字的結點插入到循環鏈表中,插入后的鏈表為:
  • 再將以 13 為關鍵字的結點插入到循環鏈表中,插入后的鏈表為:
  • 最后將以 27 為關鍵字的結點插入到循環鏈表中,插入后的鏈表為:
  • 最終形成的循環鏈表為:

 

從表插入排序的實現過程上分析,與直接插入排序相比只是避免了移動記錄的過程(修改各記錄結點中的指針域即可),而插入過程中同其它關鍵字的比較次數並沒有改變,所以表插入排序算法的時間復雜度仍是O(n2)

對鏈表進行再加工

在表插入排序算法求得的有序表是用鏈表表示的,也就注定其只能進行順序查找。而如果想用折半查找的算法,就需要對鏈表進行再加工,即對鏈表中的記錄進行重新排列,具體做法為:遍歷鏈表,將鏈表中第 i 個結點移動至數組的第 i 個下標位置中。

例如,上表是已經構建好的鏈表,對其進行再加工的過程為:

  • 首先,通過其表頭結點得知記錄中關鍵字最小的是數組下標為 4 的關鍵字 13,而 13 應該放在數組下標為 1 的位置,所以需要同下標為 1 中存放的關鍵字進行調換。但是為了后期能夠找到 49,將 13 的 next 域指向 49 所在的位置(改變之前需要保存原來的值,這里用 q 指針表示),如下表所示:
  • 然后通過 q 指針找到原本 13 指向的下一位關鍵字 27,同時 q 指針指向下標為 2 的關鍵字 38,由於 27 應該移至下標為 2 的位置,所以同關鍵字 38 交換,同時改變關鍵字 27 的 next 域,如下表所示:
  • 之后再通過 q 指針找到下一位關鍵字時,發現所指位置為下標 2,而之前已經經過了 2 次 移動,所以可以判定此時數組中存放的已經不是要找的,所以需要通過下標為 2 中的 next 域繼續尋找,找到下標為 5 的位置,即關鍵字 38,由於下標 5 遠遠大於 2,可以判斷 38 即為要找的值,所以同下標為 3 的記錄交換位置,還要更改其 next 域,同時將 q 指針指向下標為 1 的位置,如下表所示:
  • 然后通過 q 指針找到下一位關鍵字,由於其指向位置的下標 1 中的記錄已經發生移動,所以通過 next 域找到關鍵字 49,發現它的位置不用改變;同樣,當通過關鍵字 49 的 next 域找到下標為 3 的位置,還是需要通過其 next 域找到關鍵字 76 ,它的位置也不用改變。

重新排列的具體代碼實現為:

#include <stdio.h>
#include <stdlib.h>
#define SIZE 6
typedef struct
{
int rc;    //記錄項 int next;   //指針項,由於在數組中,所以只需要記錄下一個結點所在數組位置的下標即可。 }SLNode;
typedef
struct
{ SLNode r[SIZE];  
//存儲記錄的鏈表 int length;     //記錄當前鏈表長度 }SLinkListType;
// 重新排列函數 void Arrange(SLinkListType *SL)
{
// 令 p 指向當前要排列的記錄 int p = SL->r[0].next; for (int i=1; i<SL->length; i++)
   {
// 如果條件成立,證明原來的數據已經移動,需要通過不斷找 next 域,找到其真正的位置 while (p<i)
     { p
= SL->r[p].next; } // 找到之后,令 q 指針指向其鏈表的下一個記錄所在的位置 int q = SL->r[p].next; // 條件成立,證明需要同下標為 i 的記錄進行位置交換 if (p != i)
     { SLNode t; t
= SL->r[p]; SL->r[p] = SL->r[i]; SL->r[i] = t; // 交換完成后,該變 next 的值,便於后期遍歷 SL->r[i].next = p; } // 最后令 p 指向下一條記錄 p = q; } } int main(int argc, const char * argv[])
{ SLinkListType
*SL = (SLinkListType*)malloc(sizeof(SLinkListType)); SL->length = 6; SL->r[0].rc = 0; SL->r[0].next = 4; SL->r[1].rc = 49; SL->r[1].next = 3; SL->r[2].rc = 38; SL->r[2].next = 1; SL->r[3].rc = 76; SL->r[3].next = 0; SL->r[4].rc = 13; SL->r[4].next = 5; SL->r[5].rc = 27; SL->r[5].next = 2; Arrange(SL); for (int i=1; i<6; i++)
   { printf(
"%d ", SL->r[i].rc); }
return 0; }
運行結果為:   
13 27 38 49 76

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM