1. 程式人生 > >2.3.1筆記-線性表的鏈式表示和實現

2.3.1筆記-線性表的鏈式表示和實現

和順序表相比,連結串列儲存結構在實現插入、刪除的操作時,不需要移動大量資料元素(但不容易實現隨機存取線性表的第i個數據元素的操作)。所以,連結串列適用於經常需要進行插入和刪除操作的線性表,如飛機航班的乘客表等。

線性連結串列中單鏈表的結構(教材P28)

struct LNode{//線性表的單鏈表結構
    ElemType data;
    LNode *next;
};

下面程式中用到的單鏈表是帶有頭結點的單鏈表(教材P28)
程式包含了帶有頭結點的單鏈表的基本操作,這些操作包括演算法2.8~2.10,這幾種操作都是按照main函式中出現的次序排列的。
附註:為了方便測試,所有的函式都放到了一個程式裡。12個基本操作,其他的函式都是這些操作中用到的小函式,很簡單不做過多記錄。
1. InitList(LinkList &L) 構造一個空的線性表
2. ListInsert(LinkList &L,int i,ElemType e) 演算法2.9 在帶頭結點的單鏈線性表L中第i個位置之前插入元素e
3. ListTraverse(LinkList L,void (*visit)(ElemType )) 依次對L的每個資料元素呼叫函式visit(),visit()是模板函式,具體由main函式中指定
4. ListEmpty(LinkList L) 若L為空表,則返回TRUE,否則返回FALSE
5. ListLength(LinkList L) 返回L中資料元素的個數
6. DestroyList(LinkList &L) 銷燬線性表L
7. ClearList(LinkList L) 將L重置為空表(只留下頭指標和頭結點) 線性表L的結構:L+頭結點+頭結點指向的單鏈表
8. LocateElem(LinkList L,ElemType e, Status(* compare)(ElemType,ElemType)) 返回L中第1個與e滿足關係compare()的資料元素的位序,若這樣的資料元素不存在,則返回值為0 compare()是資料元素判定函式(滿足為1,否則為0)
9. GetElem(LinkList L,int i,ElemType &e) 演算法2.8 L為帶頭結點的單鏈表的頭指標。當第i個元素存在時,其值賦給e並返回ok,否則返回ERROR
10. PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e) 若cur_e是L的資料元素,且不是第一個,則用pre_e返回它的前驅,返回ok,否則操作失敗,pre_ew無定義,返回ERROR
11. NextElem(LinkList L,ElemType cur_e,ElemType &next_e) 若cur_e是L的資料元素,且不是最後一個,則用next_e 返回它的後繼,返回OK
12. ListDelete(LinkList L,int i,ElemType &e) 演算法2.10 不改變L 在帶頭結點的單鏈線性表L中,刪除第i個元素,並由e返回其值

#include<stdio.h>
#include<malloc.h>
#include<math.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;

struct LNode{//線性表的單鏈表結構
    ElemType data;
    LNode *next;
};
typedef LNode *LinkList;
void InitList(LinkList &
L){ //操作結果:構造一個空的線性表 L=(LinkList)malloc(sizeof(LNode)); if(!L)//儲存分配失敗 exit(OVERFLOW); L->next=NULL;//頭結點的指標域為空 } Status ListInsert(LinkList &L,int i,ElemType e){//演算法2.9 (教材P29)不改變L //在帶頭結點的單鏈線性表L中第i個位置之前插入元素e int j=0; LinkList s,p=L;//p指向頭結點 while(p&&j<
i-1){ j++;//計數器+1 p=p->next;//p指向下個結點 } if(!p||j>i-1) return ERROR; s=(LinkList)malloc(sizeof(LNode));//生成新結點,以下將其插入L中 s->data=e;//將e賦給新結點 s->next=p->next;//新結點指向元第i個結點 p->next=s;//原第i-1個結點指向新結點 return OK;//插入成功 } void print(ElemType e){ printf("%d ",e); } void ListTraverse(LinkList L,void (*visit)(ElemType )){ //初始條件:線性表L已存在。操作結果:依次對L的每個資料元素呼叫函式visit() LinkList p=L->next; while(p)//p所指結點存在 { visit(p->data);//對p所指結點呼叫函式visit() p=p->next;//p指向下一個結點 } printf("\n"); } Status ListEmpty(LinkList L){ //初始條件:線性表L已存在。操作結果:若L為空表,則返回TRUE,否則返回FALSE if(L->next) return FALSE; else return TRUE; } int ListLength(LinkList L){ //初始條件:線性表L已存在。操作結果;返回L中資料元素的個數 LinkList p; int i=0;//計數器初值為0 p=L->next;//p指向第1個結點 while(p){//未到表尾 i++;//計數器+1 p=p->next;//p指向下個結點 } return i; } void DestroyList(LinkList &L){ //初始條件:線性表L已存在。操作結果:銷燬線性表L LinkList q; while(L){//L指向的結點(非空) q=L->next;//q指向現線上性表的首元結點 free(L);//釋放現線上性表的頭結點 L=q;//L指向現頭結點 } } void ClearList(LinkList L){ //初始條件:線性表L已存在。操作結果:將L重置為空表(只留下頭指標和頭結點) //線性表L的結構; L+頭結點+頭結點指向的單鏈表 LinkList p=L->next;//p指向第1個結點 L->next=NULL;//將頭結點指標域為空,斷開頭結點和頭結點指向的單鏈表 DestroyList(p);//銷燬p所指的單鏈表 } int LocateElem(LinkList L,ElemType e, Status(* compare)(ElemType,ElemType)){ //初始條件:線性表L已存在,compare()是資料元素判定函式(滿足為1,否則為0) //操作結果:返回L中第1個與e滿足關係compare()的資料元素的位序 //若這樣的資料元素不存在,則返回值為0 int i=0; LinkList p=L->next;//p指向第1個結點 while(p)//未到表尾 { i++; if(compare(p->data,e))//找到這樣的資料元素 return i; p=p->next;//p指向下一個結點 } return 0;//滿足關係的資料元素不存在 } Status equal(ElemType c1,ElemType c2){ //判斷是否相等的函式 if(c1==c2) return TRUE; else return FALSE; } Status GetElem(LinkList L,int i,ElemType &e){//演算法2.8 //L為帶頭結點的單鏈表的頭指標。當第i個元素存在時,其值賦給e並返回ok,否則返回ERROR int j=1;//計數器初值為1 LinkList p=L->next;//p指向第1個結點 while(p&&j<i) { j++; p=p->next;//p指向下一個結點 } if(!p||j>i) return ERROR; e=p->data;//取第i個元素的值賦給e return OK; } Status PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e){ //初始條件:線性表L已存在 //操作結果:若cur_e是L的資料元素,且不是第一個,則用pre_e返回它的前驅,返回ok,否則操作失敗,pre_ew無定義,返回ERROR LinkList q,p=L->next; while(p->next){//p所指結點有後繼 q=p->next; if(q->data==cur_e)//p的後繼為cur_e { pre_e=p->data;//將p所指元素的值賦給pre_e return OK;// } p=q; //p的後繼不為cur_e,p向後移 } return ERROR; } Status NextElem(LinkList L,ElemType cur_e,ElemType &next_e){ //初始條件:線性表L已存在 //操作結果:若cur_e是L的資料元素,且不是最後一個,則用next_e 返回它的後繼,返回OK LinkList p=L->next;//p指向第一個結點 while(p->next) //p所指結點有後繼 { if(p->data==cur_e)//p所指結點的值為cur_e { next_e=p->next->data; return OK; } p=p->next;//p指向下個結點 } return ERROR; } Status ListDelete(LinkList L,int i,ElemType &e){//演算法2.10 不改變L //在帶頭結點的單鏈線性表L中,刪除第i個元素,並由e返回其值 int j=0;//計數器初值為0 LinkList q,p=L;//p指向頭結點 while(p->next && j<i-1){ j++; p=p->next;//p指向下一個結點 } if(!p->next || j>i-1)//刪除位置不合理 return ERROR; q=p->next; p->next=q->next;//待刪除結點的前驅指向待刪結點的後繼 e=q->data;//將待刪結點的值賦給e free(q);//釋放待刪結點 return OK;//刪除成功 } void main(){ LinkList L;//首先定義指標L ElemType e,e0; Status i; int j,k; InitList(L);//初始化線性表 for(j=1;j<=5;j++) i=ListInsert(L,1,j);//從L的表頭插入j printf("在L的表頭依次插入1~5後,L="); ListTraverse(L,print);//依次對元素呼叫print(),輸出元素的值 i=ListEmpty(L);//檢測表L是否為空 printf("L是否為空?i=%d(1:是 0:否),表L的長度=%d\n",i,ListLength(L)); ClearList(L);//清空表L printf("清空L後,L="); ListTraverse(L,print); i=ListEmpty(L);//檢測表L是否為空 printf("L是否為空?i=%d(1:是 0:否),表L的長度=%d\n",i,ListLength(L)); for(j=1;j<=10;j++) ListInsert(L,j,j);//在L的表尾插入j printf("在L的表尾依次插入1~10後,L= "); ListTraverse(L,print);//依次輸出表L中的元素 for(j=0;j<=1;j++) k=LocateElem(L,j,equal);//查詢表L中與j相等的元素,並將其在連結串列中的排序賦給k if(k) //k不為0,表明有符合條件的元素 printf("第%d個元素的值為%d\n",k,j); for(j=1;j<=2;j++)//測試頭2個數據 { GetElem(L,j,e0);//把表L中的第j個數據賦給e0 i=PriorElem(L,e0,e);//把表L中的第j個數據賦給e0 if(i==ERROR) printf("元素%d無前驅\n",e0); else printf("元素%d的前驅為%d\n ",e0,e); } for(j=ListLength(L)-1;j<=ListLength(L);j++) //最後2個數據 { GetElem(L,j,e0);//把表L中的第j個數據賦給e0 i=NextElem(L,e0,e);//求e0的後繼,如成功,將值賦給e if(i==ERROR) printf("元素%d無後繼\n",e0); else printf("元素%d有後繼%d\n",e0,e); } k=ListLength(L);//k為表長 for(j=k+1;j>=k;j--) { i=ListDelete(L,j,e);//刪除第j個數據 if(i==ERROR)//表中不存在第j個數據 printf("刪除第%d個元素失敗(不存在此元素)。",j); else //表中存在第j個數據,刪除成功,其值賦給e printf("刪除第%d個元素成功,其值為%d\n",j,e); } printf("依次輸出L的元素:"); ListTraverse(L,print);//依次輸出表L中的元素 DestroyList(L);//銷燬表L printf("銷燬L後,L=%u\n",L); }

執行截圖:
這裡寫圖片描述