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);
}
執行截圖: