一、線性表的定義
線性表(List):零個或多個數據元素的有限序列
二、線性表的抽象數據類型
ADT 線性表 (List) Data Operation lnitList(*L);//初始化操作,建立一個空的線性表 ListEmpty(L);//若線性表為空,返回true,否則返回false ClearList(*L);//清空線性表 GetElem(L,i,*e);//將線性表L中的第i個位置元素值返回給e LocateElem(L,e);//在線性表L中查找與e相等的元素,若有,返回元素的序號,否則追回0表示失敗 Listlnsert(*L,i,e);//在線性表L中的第i個位置插入新元素e ListDelete(*L,i,*e);//刪除線性表L中第i個位置元素,並用e返回其值 ListLength(L);//返回線性表L的元素個數 endADT
三、順序存儲定義
線性表的順序存儲定義:用一段地址連續的存儲單元依次存儲線性表的數據元素
1、順序存儲方式
線性表的每個數據元素的類型都相同,所以可以使用一維數據來實現順序存儲結構,即把第一個數據元素存放在數組下標為0的位置中,然后接着把線性表相鄰的元素存放在數組相鄰的位置
1、順序存儲的結構代碼 #define MAXSIZE 20 /*存儲空間初始化分配量*/ #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int Status typedef int ElemType; typedef Struct { ElemType data[MAXSIZE]; /*數組存儲數據元素*/ int length; /*線性表當前長度*/ }SqList; // 描述順序存儲結構的三個屬性 // 存儲空間的起始位置:數組data,它的位置就是存儲空間的位置 // 線性表的最大存儲容量:數組長度MAXSIZE // 線性表的當前長度:length
2、順序存儲結構的操作
初始化順序表
void lnitList(SqList L)
{
if (L == NULL) { return; }
L->length=0;
}
//算法的時間復雜度為O(1)
獲取元素
Staus GetElem(SqList L,int i,ElemType *e) { if(L.length==0 || i<1 || i>L.length) return ERROR ; *e=L.data[i-1] ; return OK ; }
//算法的時間復雜度為O(1)
插入操作
// 算法思路:插入位置不合理,拋出異常 // 如果線性表長度大於等於數組長度,則拋出異常或動態增加容量 // 從最后一個元素向前遍歷到第i個位置,分別將他們向后移動一個位置 // 將要插入的元素填入i處
// 表長加1 Status Listlnsert(SqList L,int i,ElemType *e) { int k; if (L->length==MAXSIZE)return ERROR;/*線性表已滿*/ if (i<1 || i>L->length+1)return ERROR;/*當i不在范圍*/ if (1<=L->length)/*當i不在表尾*/ { for (k=L->length-1; k>=i-1; k--) { L->data[K+1]=L->data[k]; } } L->data[i-1]=e;/*插入新元素*/ L->length++;/*表長加一*/ return OK; }
//算法的時間復雜度為O(n)
刪除操作
// 算法思路:刪除位置不合理,拋出異常 // 取出刪除的元素 // 從刪除元素位置開始遍歷到最后一個元素的位置 // 分別將他們向前移動一個位置 // 表長減一 Status ListDelete(SqList L,int i,ElemType *e) { int k; if (L->length==0)return ERROR;/*線性表為空*/ if (i<1 || i>L->length)return ERROR;/*當i不在范圍*/ *e=L->data[i-1]; if (1<L->length)/*當i不在表尾*/ { for (k=i; k<L->length; k++) { L->data[K-1]=L->data[k]; } } L->length--;/*表長減一*/ return OK; }
//算法的時間復雜度為O(n)
線性表的順序存儲結構在存、讀數據的時候,不管是哪個位置,時間復雜度都為0(1);而插入或刪除時,時間復雜度都是0(n)
四、線性表的順序存儲結構的優缺點和特點
優點:無須為表示表中元素之間的邏輯關系為增加額外的存儲空間
可以快速地存取表中任一位置的元素
缺點:插入和刪除操作需要移動大量元素
當線性表長度變化較大時,難以確定空間的容量
造成存儲空間的“碎片”
五、線性表的鏈式存儲結構定義
為了表示每個數據元素與它的后繼數據元素的邏輯關系,對於數據元素a來說,除了存儲其本身的信息之外(數據域),還需要存儲一個指示其直接后繼的存儲位置(指針域)。這兩部分信息組成數據元素a的存儲映像,稱為結點(Node)。
n個結點鏈成一個鏈表,即為線性表(a1,a2,a3,...,an)的鏈式存儲結構
1、單鏈表
在每個結點中除包含有數據域外,只設置一個指針域,用以指向其后繼結點
頭指針:鏈表中的第一個結點的存儲位置叫做頭指針
頭結點:在單鏈表的第一個結點前附設一個結點,叫做頭結點
/*線性表的單鏈表存儲結構*/
typedef struct Node { ElemType data; struct Node *next; }Node; typedef struct Node *LinkList;/*定義LinkList*/
1.1單鏈表的創建
(1)頭插法建立單鏈表
從一個空表開始,讀取數組a的元素,生成新結點,將讀取的數據放在新結點的數據域中,然后將新結點插入到當前鏈表的表頭,直到結束為止
void CreateListF(LinkList *&L,ElemType a[],int n) { LinkList *s;int i; L=(LinkList*)malloc(sizeof(LinkList));/*創建頭結點*/ L->next=NULL; for (i = 0; i < n; i++) { s=(LinkList*)malloc(sizeof(LinkList));/*創建新結點*/ s->data=a[i]; s->next=L->next; L->next=s; } }
(2)尾插法建立單鏈表
從一個空表開始,讀取數組a的元素,生成新結點,將讀取的數據放在新結點的數據域中,然后將新結點插入到當前鏈表的尾結點,但是要增加一個尾結點指針r。使他始終指向當前鏈表的表尾上,直到結束為止
void CreateListR(LinkList *&L,ElemType a[],int n) { LinkList *s,*r;int i; L=(LinkList*)malloc(sizeof(LinkList));/*創建頭結點*/ r=L;/*r始終指向尾結點,開始時指向頭結點*/ for (i = 0; i < n; i++) { s=(LinkList*)malloc(sizeof(LinkList));/*創建新結點*/ s->data=a[i]; r->next=s;/*將*s插入到*r之后*/ r=s; } r->next=NULL;/*尾結點的next域設置為NULL*/ }
1.2單鏈表的讀取
獲取第i個數據的算法思路
-
- 聲明一個指針p指向鏈表第一個結點,初始化j從1開始
- 當j<i時,就遍歷鏈表,讓p指針向后移動,不斷指向先一個結點,j累加1
- 若到鏈表末尾p為空,說明第i個結點不存在
- 否則查找成功,返回結點p的數據
Status GetElem(LinkList L,int i,ElemType *e) { int j; LinkList p;/*聲明一個指針p*/ p=L->next;/*讓p指向鏈表L的第一個結點*/ j=1; while(p && j<1) { p=p->next; ++j; } if (!p || j>i)return ERROR; *e=p->data;return OK; }
1.3單鏈表的插入與刪除
單鏈表第i個數據插入結點的算法思路
- 聲明一個指針p指向鏈表的頭結點,初始化j從1開始
- 當j<i時,就遍歷鏈表,讓p指針向后移動,不斷指向先一個結點,j累加1
- 若到鏈表末尾p為空,說明第i個結點不存在
- 否則查找成功,在系統中生成一個空結點s
- 將數據元素e賦值給s->data
- 單鏈表的插入標准語句s->next=p->next;p->next=s;
- 返回成功
Status Listlnsert(LinkList *L,int i,ElemType e) { int j; LinkList p,s; p= *L; j= 1; while(p && j<i) { p=p->next; ++j; } if (!p || j>i)return ERROR; s=(LinkList)malloc(sizeof(LinkList)); s->data=e; s-next=p->next;/*將p的后繼結點賦值給s的后繼*/ p->next=s;/*將s賦值給p的后繼*/ return OK; }
單鏈表第i個數據刪除結點的算法思路
- 聲明一個指針p指向鏈表的頭結點,初始化j從1開始
- 當j<i時,就遍歷鏈表,讓p指針向后移動,不斷指向先一個結點,j累加1
- 若到鏈表末尾p為空,說明第i個結點不存在
- 否則查找成功,將欲刪除的結點p->next賦值給q
- 單鏈表的刪除標准語句p->next=q->next
- 將q結點中的數據賦值給e,作為返回
- 釋放q結點
- 返回成功
Status ListDelete(LinkList *L,int i,ElemType *e) { int j; LinkList p,q; p= *L; j= 1; while(p->next && j<i) { p=p->next; ++j; } if (!(p->next) || j>i)return ERROR; q=p->next; p-next=q->next; *e=q->data; free(q); return OK; }
1.4單鏈表的整表刪除
單鏈表的整表刪除算法思路
- 聲明一個結點p和q
- 將第一個結點賦值給p
- 循環
-
- 將下一個結點賦值給q;
- 釋放p
- 將q賦值給p
Status ClearList(LinkList *L) { LinkList p,q; p=(*L)->next; while(p) { q=p->next; free(p); p=q; } (*L)->next=NULL; return OK; }
2、雙鏈表
在每個結點中除包含數值域外,設置有兩個指針域,分別用以指向其前驅結點和后繼結點
//雙鏈表結點類型DLinkList的聲明如下 typedef struct DLinkList { ElemType data; struct DLinkList *prior; //指向前驅結點 struct DLinkList *next; //指向后繼結點 }DLinkList;
2.1雙鏈表的創建
(1)頭插法建立雙鏈表
從一個空表開始,讀取數組a的元素,生成新結點,將讀取的數據放在新結點的數據域中,然后將新結點插入到當前鏈表的表頭,直到結束為止
void CreateDListF(LinkList *&L,ElemType a[],int n) { DLinkList *s;int i; L=(DLinkList*)malloc(sizeof(DLinkList));/*創建頭結點*/ L->next=L->prior=NULL; for (i = 0; i < n; i++) { s=(DLinkList*)malloc(sizeof(DLinkList));/*創建新結點*/ s->data=a[i]; s->next=L->next; if (L->next!=NULL) { L->next->prior=s; } L->next=s; s->prior=L; } }
(2)尾插法建立雙鏈表
從一個空表開始,讀取數組a的元素,生成新結點,將讀取的數據放在新結點的數據域中,然后將新結點插入到當前鏈表的尾結點,但是要增加一個尾結點指針r。使他始終指向當前鏈表的表尾上,直到結束為止
void CreateDListR(LinkList *&L,ElemType a[],int n) { DLinkList *s,*r;int i; L=(DLinkList*)malloc(sizeof(DLinkList));/*創建頭結點*/ r=L; for (i = 0; i < n; i++) { s=(DLinkList*)malloc(sizeof(DLinkList));/*創建新結點*/ s->data=a[i]; r->next=s; s->prior=r; r=s; } r->next=NULL; }
2.2雙鏈表的基本運算法則
(1)查找指定元素的結點
在雙鏈表中查找第一個data域值為X的結點、從第一個結點開始。變遍歷邊比較。若找到這樣的結點。則返回序列。否則返回0
int Finfnode(DLinkList *L,ElemType X) { LinkList *P=l->next; int i=1; while(p!=NULL&&p->data!=x) { i++; p=p->next; } if (p==NULL)return 0; else return i; }
(2)插入結點操作
s->next=p->next; //將*s插入到*p之后 p->next-prior=s; s->prior=p; p->next=s;
//在雙鏈表L中第i個位置插入值域為e的結點 int Listlnsert(DLinkList *&L,int i,ElemType e) { int j=0; DLinkList *p=L,*s; while(j<i-1 && p!=NULL) //查找第i-1個元素 { j++; p=p->next; } if (p==NULL)return 0; //未找到第i-1個節點 else //找到第i-1個節點 { s=(DLinkList *)malloc(sizeof(DLinkList)); //創建新結點*s s->data=e; s->next=p->next; //將*s插入到*p之后 if (p->next!=NULL) //若存在*p的后繼結點。將其前驅指向*s { p->next->prior=s; } s->prior=p; p->next=s; return 1; } }
(3)刪除結點操作
p->next=q->next; //刪除*p結點后的結點 q->next-prior=p;
//在雙鏈表L中刪除第i個結點 int ListDelete(DLinkList *&L,int i,ElemType e) { DLinkList *p=L,*q; int j=0; while(j<i-1 && p!=NULL) //查找第i-1個元素 { j++; p=p->next; } if (p==NULL)return 0; //不存在第i-1個節點 else //找到第i-1個節點*p { q=p->next; //q指向要刪除的結點 if (q==NULL)return 0; //不存在第i個結點 p->next=q->next; //從鏈表中刪除*q結點 if (p->next!=NULL) p->next->prior=p; free(q); //釋放*q結點 return 1; } }