1. 程式人生 > 實用技巧 >資料結構:線性表(連結串列)

資料結構:線性表(連結串列)

1、連結串列

(1)概念

  • 結點在儲存器中的位置是任意的,即邏輯上相鄰的資料元素在物理上不一定相鄰
  • n 個結點由指標鏈組成一個連結串列。它是線性表的鏈式儲存映像,稱為線性表的鏈式儲存結構

(2)結點組成

  • 資料域:儲存元素數值資料
  • 指標域:儲存直接後繼結點的儲存位置

(3)單鏈表、雙鏈表與迴圈連結串列:

  • 結點只有一個指標域的連結串列,稱為單鏈表或線性連結串列
  • 有兩個指標域的連結串列,稱為雙鏈表。雙向連結串列能夠克服單鏈表查詢前驅結點必須從表頭出發的問題,雙向連結串列有兩個指標域分別指向自己的前驅結點和後繼結點,查詢的時間複雜度為O(1)
  • 首尾相接的連結串列稱為迴圈連結串列,從迴圈連結串列中的任何一個結點的位置都可以找到其他所有結點,而單鏈表做不到

(4)頭指標、頭結點和首元結點

  • 頭指標是指向連結串列中第一個結點的指標
  • 首元結點是指連結串列中儲存第一個資料元素a1的結點
  • 頭結點是在連結串列的首元結點之前附設的一個結點,資料域內只放空表標誌和表長等資訊。頭結點的資料域可以為空,也可存放線性表長度等附加資訊,但此結點不能計入連結串列長度值。

(5)在連結串列中設定頭結點有什麼好處

  • 便於首元結點的處理:首元結點的地址儲存在頭結點的指標域中,所以在連結串列的第一個位置上的操作和其它位置一致,無須進行特殊處理
  • 便於空表和非空表的統一處理:無論連結串列是否為空,頭指標都是指向頭結點的非空指標,因此空表和非空表的處理也就統一了

(6)特點

  • 結點在儲存器中的位置是任意的,即邏輯上相鄰的資料元素在物理上不一定相鄰
  • 訪問時只能通過頭指標進入連結串列,並通過每個結點的指標域向後掃描其餘結點,所以尋找第一個結點和最後一個結點所花費的時間不等

(7)優缺點

優點:

  • 資料元素的個數可以自由擴充
  • 插入、刪除等操作不必移動資料,只需修改連結指標,修改效率較高

缺點:

  • 儲存密度小
  • 存取效率不高,必須採用順序存取,即存取資料元素時,只能按連結串列的順序進行訪問

(8)結構定義

typedef struct Lnode
{
     ElemType   data;       //資料域
     struct LNode  *next;   //
指標域 }LNode,*LinkList; // *LinkList為Lnode型別的指標

(9)初始化

Status InitList_L(LinkList &L){ 
   L=new LNode;  //生成新結點作頭結點,用頭指標L指向頭結點。                      
   L->next=NULL; //頭結點的指標域置空    
   return OK; 
} 

2、連結串列的基本操作

(1)銷燬

Status DestroyList_L(LinkList &L)
{
    LinkList p;
       while(L)
        {
            p=L;  
            L=L->next;
            delete p;  
        }
     return OK;
 }

(2)清空

Status ClearList(LinkList & L){
  // 將L重置為空表 
   LinkList p,q;
   p=L->next;   //p指向第一個結點
   while(p)       //沒到表尾 
   { 
q=p->next;
delete p;
p=q;
} L
->next=NULL; //頭結點指標域為空 return OK; }

與銷燬不同,清空的時候會保留頭結點

(3)求表長

p=L->next;  
i=0;             
while(p)
{i++;
p=p->next;
}
int  ListLength_L(LinkList L){
//返回L中資料元素個數
    LinkList p;
    p=L->next;  //p指向第一個結點
     i=0;             
     while(p){//遍歷單鏈表,統計結點數
           i++;
           p=p->next;    
}
return i; }

(4)判斷是否為空

int ListEmpty(LinkList L)
{ 
    //若L為空表,則返回1,否則返回0 
   if(L->next)   //非空 
     return 0;
   else
     return 1;
 }

3、對連結串列的操作

(1)新增元素:在L中第i個元素之前插入資料元素e

Status ListInsert_L(LinkList &L,int i,ElemType e){ 
     p=L;j=0; 
      while(p&&j<i−1){p=p->next;++j;}    //尋找第i−1個結點 
      if(!p||j>i−1)return ERROR;    //i大於表長+1或者小於1  
      s=new LNode;            //生成新結點s 
      s->data=e;                         //將結點s的資料域置為e 
      s->next=p->next;                     //將結點s插入L中 
      p->next=s; 
      return OK; 
}//ListInsert_L 

時間複雜度:O(1)

(2)刪除元素:將線性表L中第i個數據元素刪除

 Status ListDelete_L(LinkList &L,int i,ElemType &e){
    p=L;j=0; 
    while(p->next &&j<i-1){                  //尋找第i個結點,並令p指向其前驅 
        p=p->next; ++j; 
    } 
    if(!(p->next)||j>i-1) return ERROR; //刪除位置不合理 
    q=p->next;                                        //臨時儲存被刪結點的地址以備釋放 
    p->next=q->next;                       //改變刪除結點前驅結點的指標域 
    e=q->data;                                     //儲存刪除結點的資料域 
    delete q;                                     //釋放刪除結點的空間 
 return OK; 
}//ListDelete_L 

時間複雜度:O(1)

(3)查詢:線上性表L中查詢值為e的資料元素

int LocateELem_L (LinkList L,Elemtype e) {
 //返回L中值為e的資料元素的位置序號,查詢失敗返回0 
  p=L->next; j=1;
  while(p &&p->data!=e)  
        {p=p->next;  j++;}                  
  if(p) return j; 
  else return 0;
} 

時間複雜度:O(n)

4、單鏈表的建立

(1)前插法

void CreateList_F(LinkList &L,int n){ 
     L=new LNode; 
      L->next=NULL; //先建立一個帶頭結點的單鏈表 
      for(i=n;i>0;--i){ 
        p=new LNode; //生成新結點 
        cin>>p->data; //輸入元素值 
        p->next=L->next;L->next=p;     //插入到表頭 
     } 
}//CreateList_F 
  • 生成新結點
  • 將讀入資料存放到新結點的資料域中
  • 將該新結點插入到連結串列的前端

(2)尾插法

void CreateList_L(LinkList &L,int n){ 
      //正位序輸入n個元素的值,建立帶表頭結點的單鏈表L 
      L=new LNode; 
      L->next=NULL;     
      r=L;                                     //尾指標r指向頭結點 
      for(i=0;i<n;++i){ 
        p=new LNode;                //生成新結點 
        cin>>p->data;                  //輸入元素值 
        p->next=NULL; r->next=p;       //插入到表尾 
        r=p;                                       //r指向新的尾結點 
      } 
}//CreateList_L 
  • 從一個空表L開始,將新結點逐個插入到連結串列的尾部,尾指標r指向連結串列的尾結點
  • 初始時,r同L均指向頭結點。每讀入一個數據元素則申請一個新結點,將新結點插入到尾結點後,r指向新結點

5、線性表和連結串列的比較

(1)空間

  • 儲存空間:順序表需要預先分配,會導致空間閒置或溢位現象,連結串列動態分配,不會出現儲存空間閒置或溢位現象
  • 儲存密度:線性表等於1,連結串列小於1

(2)時間

  • 取資料:順序表是隨機存取,時間複雜度為O(1),連結串列是順序存取,時間複雜度為O(n)
  • 插入刪除:順序表需要移動元素,時間複雜度為O(n),連結串列不需要,時間複雜度為O(1)

(3)適用範圍

線性表

表長變化不大,且能事先確定變化的範圍
很少進行插入或刪除操作,經常按元素位置序號訪問資料元素

連結串列

長度變化較大
頻繁進行插入或刪除操作