05.線性表(四)鏈式儲存結構.靜態鏈表,線性鏈式
鏈式儲存結構.靜態鏈表 一、靜態鏈表1.靜態鏈表格儲存體結構 單鏈表是通過指標實現的,但是我們也可以通過數組來代替指標描述單鏈表,即靜態鏈表。如何?靜態鏈表?構造數組的元素由兩個資料域組成:data和cur,即數組的每個下標都對應一個data和一個cur。資料域data:用來存放資料元素,即要處理的資料;遊標cur:存放該元素的後繼在數組中的下標,相當於單鏈表中的next指標;為了方便插入資料,我們通常會把數組建立得大一些,以便有一些空閑空間而不致於出現溢出情況。線性表的靜態鏈表格儲存體結構:#define MAXSIZE 1000 //假設鏈表的長度為1000(個元素)typedef struct{ ElemType data; //資料域,int類型 int cur; //遊標(Cursor),為0時表示無指向}Component,StaticLinkList(MAXSIZE);2.備用鏈表 由於數組的第一個和最後一個元素作為特殊元素處理,不存資料,因此我們把未使用的數組元素稱為備用鏈表。因此,我們規定:(1)數組第一個元素(即下標為0的元素)的遊標cur存放第一個空閑空間元素的下標(備用鏈表的第一個元素);(2)數組最後一個元素的遊標cur存放第一個有數值的元素的下標(相當於單鏈表中的頭結點作用)。當整個鏈表為空白時則最後一個元素的遊標cur為0。(3)鏈表的最後一個有值元素的cur為0
升華筆記:如何將一維數組list中各分量鏈成一個備用鏈表?typedef int StatusStatus InitList(StaticLinkList list){ int i; //i為數組下標,MAXSIZE為鏈表長度 for(i=0;i<MAXSIZE-1;i++) { list[i].cur=i+1; // 將數組第一個元素的遊標cur指向備用鏈表的第一個結點儲存位置(數組下標),依次類推 } list[MAXSIZE-1].cur=0; //目前靜態鏈表為空白,最後一個有值元素的cur為0 }
二、靜態鏈表的插入/刪除操作 靜態鏈表的插入和刪除操作,最關鍵是要解決如何用靜態類比動態鏈表結構的儲存空間的分配,需要時申請,無用時釋放。
1.靜態鏈表的插入操作
(1)演算法思路 為了辨明數組中哪些分量未被使用,解決的辦法是將所有違背使用過的及已被刪除的分量用遊標cur鏈成一個備用的鏈表(即空鏈表),每當進行插入時,便可以從備用鏈表上取得第一個結點(即未被使用的第一個結點)最為待插入新結點。實現擷取空閑分量下標Malloc_SLL函數演算法:a.擷取數組第一個元素的遊標cur=i,其存放的是備用鏈表的第一個空閑結點;b.將數組第i個元素的遊標cur=i+1賦值給頭指標c.返回被使用的數組元素下標
int i=list[0].cur; //如i=list[0].cur=7 list[0].cur=list[i].cur; //頭指標list[0].cur=list[7].cur=8 return i;
(2)源碼實現/*1.若備用空間鏈表為空白,則返回分配的結點下標,否則返回0*/int Malloc_SLL(StaticLinkList list){ int i=list[0].cur; //擷取備用鏈表的第一個結點下標(當前數組第一個元素的cur儲存第一個備用閒置下標) if(list[0].cur) //如果list[0].cur!=0,則說明數組含有非空元素 { list[0].cur=list[i].cur; //由於要拿出一個備用鏈表的結點使用,我們需要將數組第一個元素的cur存放下一個空出來的元素作備用 } return i;//返回被使用的下標}
//注釋:假如先前list[0].cur=7(數組下標值),當下標為7的分量(數組元素)準備被使用了,就得有接替者,所以把分量7(list[i].cur,其中i=7)的cur值=8,賦值給頭元素(list[0].cur),之後就可以繼續分配新的空閑分量。///*2.在L中第i個元素之前插入新的資料元素e*/typedef int Statustypedef int ElemTypeStatus ListInsert(StaticLinkList L,int i,ElemType e){ int j,k,m; k=MAX_SIZE-1; //注意:k首先是最後一個元素的下標 if(j<1 || j>ListLength(L)+1) return ERROR; j=Malloc_SLL(L); //a.獲得空閑分量的下標 if(j) { L[j].data=e; //b.將資料賦值給此分量的data for(m=1;m<i-1;i++) //c.找到第i個元素之前的位置,將數組最後一個元素的cur(=1)存到變數k k=L[k].cur; //k=1 L[j].cur=L[k].cur; //將第i個元素之前的cur賦值給新元素的cur L[k].cur=j; //把新元素的下標賦值給第i個元素 return OK; } return ERROR;}注釋:第i個元素,是指鏈表的第i個元素,非數組下標儲存位置。 i-插入靜態鏈表位置;j-儲存空間空閑位置;
2.靜態鏈表的刪除操作源碼實現/*1.將下標為k的空閑結點回收到備用鏈表*/void Free_SSL(StaticLinkList space,int k){ space[k].cur=space[0].cur; //將資料的第一個元素cur(其值為備用鏈表的第一個空閑元素下標),賦值給要刪除分量的cur space[0].cur=k; //把要刪除的分量下標賦值給第一個元素的cur}/*2.刪除在L中第i個資料元素e*/typedef int StatusStatus ListDelete(StaticLinkList L,int i){ int i,k; if(i<1 || i>ListLength(L)) return ERROR; k=MAXSIZE-1; //儲存鏈表最後一個元素的下標 for(j=1;j<=i-1;j++) k=L[k].cur; //找到要刪除元素的前一個元素,並將其cur值賦值給k(即要刪除元素的下標) j=L[k].cur; //將刪除元素的遊標值賦值給j,即值為下一個元素的下標 L[k].cur=L[j].cur; Free_SSL(L,j);}/*3.初始條件:靜態鏈表L已經存在。操作結果:返回L中資料元素個數*/int ListLength(StaticLinkList L){ int j=0; int i=L[MAXSIZE-1].cur; while(i) { i=L[i].cur; j++; } return j;}
三、靜態鏈表的優缺點
1.優點 在插入和刪除操作時只需要修改遊標,不需要移動元素,從而改進了在順序儲存結構中的插入和刪除操作需要移動大量元素的缺點;
2.缺點(1)沒有解決連續儲存分配帶來的表長度難以確定的問題;(2)失去了順序儲存結構隨機存取的特性;