資料結構:線性表(連結串列)
阿新 • • 發佈:2020-07-24
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)適用範圍
線性表
表長變化不大,且能事先確定變化的範圍
很少進行插入或刪除操作,經常按元素位置序號訪問資料元素
連結串列
長度變化較大
頻繁進行插入或刪除操作