第二章 線性表
參考文獻:[資料結構(C語言版)].嚴蔚敏
本篇章僅為個人學習資料結構的筆記,不做任何用途。
2.1 線性結構的特點
(1). 存在唯一的一個被稱為"第一個"的資料元素
(2). 存在唯一的一個被稱為"最後一個"的資料元素
(3). 除第一個之外,集合中的每個資料元素均有唯一的前驅
(4). 除最後一個之外,集合中的每個資料元素均有唯一的後繼
2.2 線性表的類型定義
線性表(linear_list) 是最簡單最常用的一種資料結構。一個線性表是n個資料元素的有限序列。
例如,26個英文字母的字母表:
(A,B,C,...,Z)
上述是一個線性表,資料元素是單個字母字元。
例如,一個家庭中各個家庭成員的年齡,可用線性表的形式給出:
(6,17,32,38,48,70,96)
上述線性表中的資料元素是整數。
複雜的線性表情況如下:
- 一個資料元素有若干個 資料項目(item) 組成。
- 如上情況,資料元素稱為 記錄(record)。
- 含有大量記錄的線性表稱為 檔案(file)。
例如,一個學生的學生健康情況登記表。表中每個學生的情況為一個記錄。
它由姓名,學號,性別,年齡,班級等5個資料項目組成。
| 姓名 |
學號 |
性別 |
年齡 |
班級 |
| 張盛東 |
790302 |
男 |
28 |
計科062 |
| 盧曄 |
790303 |
女 |
26 |
計科061 |
因此,線性表的抽象描述如下:
\[ (a_{1},...,a_{i-1},a_{i},a_{i+1},...,a_{n}) \]
當i=1,2,...,n-1時,ai有且僅有一個直接後繼。
當i=2,3,...,n時,ai有且僅有一個直接前驅。
線性表中元素的個數n(n>=0)定義為線性表的長度,n=0為空白表。
非空表中的每個資料元素都有一個確定的位置。如a1是第一個資料元素,如an是最後一個資料元素,ai是第i個元素,i為資料元素ai線上性表的位序。
抽象資料類型的結構:
ADT 抽象資料類型名{ 資料對象:<資料對象的定義> 資料關係:<資料關係的定義> 基本操作:<基本操作的定義>} ADT 抽象資料類型名
抽象資料類型線性表的定義:
ADT List{ 資料對象:D ={a(i)|a(i) 屬於ElemSet, i=1,2,...,n,n>=0} 資料關係:R ={<a(i-1),a(i)>|<a(i-1),a(i) 屬於D,i=2,...,n} 基本操作: InitList(&L) 操作結果:構造一個空的線性表L。 DestoryList(&L) 初始條件:線性表L已存在。 操作結果:銷毀線性表L。 ClearList(&L) 初始條件:線性表L已存在。 操作結果:將L重設為空白表。 ListEmpty(L) 初始條件:線性表L已存在。 操作結果:判斷L是否為空白表,是的話返回True,不是返回False。 ListLength(L) 初始條件:線性表L已存在。 操作結果:返回L中資料元素的個數。 GetElem(L,i,&e) 初始條件:線性表L已存在,1<=i<=ListLength(L) 操作結果:用e返回L中第i個資料元素的值。 LocateElem(L,e,compare()) 初始條件:線性表L存在,compare()是資料元素判定函數。 操作結果:返回L中第一個與e滿足關係compare()的資料元素的位序。若這樣的資料元素不存在,則傳回值為0。(通俗來講,就是返回資料元素的位置) PriorElem(L,cur_e,&pre_e) 初始條件:線性表L已存在。 操作結果:若cur_e是L的資料元素,且不是第一個,則用pre_e返回它的前驅,否則操作失敗,pre_e不存在。(通俗來講,返回資料元素的前驅) NextElem(L,cur_e,&next_e) 初始條件:線性表L已存在。 操作結果:若cur_e是L的資料元素,且不是最後一個,則用next_e返回它的後繼,否則操作失敗,next_e不存在。(通俗來講,返回資料元素的後繼) ListInert(&L,i,e) 初始條件:線性表L已存在,1<=i<=ListLength(L)+1。 操作結果:在L中第i個位置之前插入新的資料元素e,L的長度加1。 ListDelete(&L,i,&e) 初始條件:線性表L已存在,1<=i<=ListLength(L)。 操作結果:刪除L中第i個資料元素,並用e返回其值,L的長度減1。 ListTraverse(L,visit()) 初始條件:線性表L已存在。 操作結果:依次對L的每個資料元素調用visit()。一旦visit()失敗,則操作失敗。 (通俗來講,對每一個資料元素進行遍曆) } ADT List
2.3 線性表的順序表示和實現
線性表的順序表示 指的是用一組地址連續的儲存單元依次儲存線性表的資料元素。
假設線性表的每個元素需要用l個儲存單元,並以所佔的第一個單元的儲存地址作為資料元素的儲存位置。則第i+1個資料元素的儲存位置LOC(a1+1)和第i個資料元素的儲存位置LOC(ai)滿足如下關係:
\[ LOC(a_{i+1})=LOC(a_{i})+l\]
一般來說,線性表中的第i個資料元素的儲存位置為:
\[ LOC(a_{i})=LOC(a_{1})+(i-1)*l\]
上述的這種表述稱為線性表的順序結構。亦或者稱為順序映像(sequential mapping)。其特點為:
相鄰的兩個資料元素ai和a1+1。其儲存的"物理位置"也相鄰。如下:
由於確定了儲存線性表的起始位置,每一個資料元素的儲存位置都和線性表的起始位置相差一個和資料元素線上性表中的位序成正比的常數,線性表中任一資料元素都可隨機存取,所以線性表的順序結構是一種隨機存取的儲存結構。
C語言中可用動態分配的一維數組,來描述線性表的順序儲存結構,其中數組指標elem指示線性表的基地址,length代表線性表的當前長度,如下:
// - - - - - 線性表的動態分配順序儲存結構 - - - - -#define LIST_INIT_SIZE 100 //線性表格儲存體空間的初始分配量#define LISTINCREMENT 10 //線性表格儲存體空間的分配增量typedef struct { ELemType * elem; //儲存空間基址 int length; //當前長度 int listsize; //當前分配的儲存容量(以sizeof(ElemTpye)為單位)}SqList;
如下演算法用於構造一個空的線性表。初始化操作是為順序表分配一個預定義大小的數組空間,並將線性表的當前長度設為"0"。listsize為當前分配的儲存空間大小。
Status InitList_Sq(SqList &L){ //構造一個空的線性表L。 L.elem = (ELemType *)malloc(LIST_INIT_SIZE * sizeof(ELemType)); if(! L.elem) exit(OVERFLOW); //儲存分配失敗 L.length = 0; //空表長度為0 L.listsize = LIST_INIT_SIZE; //初始化儲存容量 return OK;}// InitList_Sq
線性表的順序儲存的插入操作:
例如:線上性表的第i-1個資料元素和第i個資料元素之間插入一個新的資料元素,會使得長度為n的線性表。
\[ (a_{1},...,a_{i-1},a_{i},...,a_{n}) \]
變成長度為n+1的線性表
\[ (a_{1},...,a_{i-1},b,a_{i},...,a_{n}) \]
線上性表中的順序儲存結構中,由於邏輯上相鄰的資料元素在物理位置上也是相鄰的。因此,插入資料元素,儲存位置有如下的變化。
順序線性表的插入演算法:
Status ListInsert_Sq(SqList &L,int i,ElemType e){ //在順序線性表中L中第i個位置之前插入新的元素e。 //i的合法值為1<=i<=ListLength_Sq(L)+1 if(i < 1 || i >L.length+1) return ERROR; //i值不合法 if(L.length >L.listsize){ newbase = (ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT) * sizeof(ElemTpye)); if(! newbase)exit(OVERFLOW); //儲存分配失敗 L.elem = newbase; //新基址 L.listsize += LISTINCREMENT; //增加儲存容量 } q = &(L.elem[i-1]); //q為插入位置 for (p = &(L.elem[L.length-1]);p >= q; --p) *(p+1) =*p; //插入位置及之後的元素後移 * q =e; //插入e ++L.length; //線性表長度+1 return OK;}// ListInsert_Sq
線性表的順序儲存的刪除操作:
例如:線上性表中刪除一個資料元素,會使得長度為n的線性表。
\[ (a_{1},...,a_{i-1},a_{i},a_{i+1}...,a_{n}) \]
變成長度為n-1的線性表
\[ (a_{1},...,a_{i-1},a_{i+1},...,a_{n}) \]
刪除資料元素,儲存位置有如下變化。
順序線性表的刪除演算法:
Status ListDelete_Sq(SqList &L,int i,ElemType &e){ //在順序線性表中L中刪除第i個元素,並用e返回其值。 //i的合法值為1<=i<=ListLength_Sq(L) if(i < 1 || i >L.length+1) return ERROR; //i值不合法 p = &(L.elem[i-1]); //q為刪除元素的位置 e = *p; //被刪除元素的值賦予e q = L.elem+L.length-1; //表尾元素的位置 for(++p;p<=q,++p) *(p-1) = *p; //被刪除元素之後的位置左移 --L.length; //線性表長度-1 return OK;}// ListDelete_Sq
線性表的順序儲存的尋找操作:
順序線性表的尋找演算法:
int LocateElem_Sq(SqList L,ElemType e,Status (*compare)(ElemType,ElemType)){ //在順序線性表中L中尋找第一個與e滿足compare()的元素的位序。 //若找到,則返回其在L中的位序,否則返回0。 i = 1 //i的初值為第一個元素的位序。 p = L.elem; //p的初值為第一個元素的儲存位置。 while(i<=L.length && !(*compare)(* p++,e)) ++i; if(i<=L.length) return i; else return 0;}// LocateElem_Sq
線性表的順序儲存的合併作業:
void MergeList_Sq(SqList La,Sqlist Lb,SqList &Lc) { // 已知順序線性表La和Lb的元素按值非遞減排列 //歸併La和Lb得到的新的順序線性表Lc,Lc的元素也按值非遞減排列 pa =La.elem; pb=Lb.elem; Lc.listsize =Lc.length = La.length+Lb.length; pc = Lc.elem =(ElemTpye *)malloc(Lc.listsize *sizeof(ElemTpye)); if(!Lc.elem)exit(OVERFLOW); //儲存分配失敗 pa_last = La.elem + La.length -1; pb_last = Lb.elem + Lb.length -1; while(pa <= pa_last && pb <= pb_last){ //歸併 if( *pa <= *pb) *pc++ = *pa++; else *pc++ = *pb++; } while(pa <= pa_last) *pc++ = *pa++; while(pb <= pb_last) *pc++ = *pb++;} //MergeList_Sq