靜態鏈表

以前學習的各種鏈表都是由指針實現的,鏈表中結點的分配和回收(即釋放)都是由系統提供的標準函數malloc和free動態實現的,故稱之爲動態鏈表。但是有的高級語言,如BASIC、FORTRAN等,沒有提供”指針”這種數據類型,此時若想採用鏈表做存儲結構,就必須使用”遊標”來模擬指針,由程序員自己編寫”分配結點”和”回收結點”的過程。
用遊標實現鏈表,其方法是:定義一個較大的結構數組作爲備用結點空間(即存儲池)。當申請結點時,每個結點應含有兩個域:data域和cursor域。data域用來存放結點的數據信息,需注意的是,此時的cursor域不在是指針而是遊標指示器,遊標指示器指示其後繼結點在結構數組中的相對位置(即數組下標)。數組的第0個分量可以設計成表的頭結點,頭結點的next域指示了表中第一個結點的位置。表中當前最後一個結點的域爲0,表示靜態單鏈表的結束。我們把這種用遊標指示器實現的單鏈表叫做靜態單鏈表,static linked list。靜態單鏈表同樣可以藉助一維數組來描述:

#define Maxsize = 鏈表可能達到的最大長度
typedef struct
{
    ElemType data;
    int cursor;
}Component, StaticList[Maxsize];

假如有如上的靜態鏈表S中存儲這線性表(a,b,c,d,e,f,g,h,i),Maxsize=11,如圖所示,要在第四個元素後插入元素e,方法是:先在當前表尾加入一個元素e,即:S[9].data = e;然後修改第四個元素的遊標域,將e插入到鏈表中,即:S[9].cursor = S[4].cursor; S[4].cursor = 9;,接着,若要刪除第8個元素h,則先順着遊標鏈通過計數找到第7個元素存儲位置6,刪除的具體做法是令S[6].cursor = S[7].cursor。

上述例子中未考慮對已釋放空間的回收,這樣在經過多次插入和刪除後,會造成靜態鏈表的”假滿”,即表中有很多空閒空間,但卻無法再插入元素。造成這種現象的原因是未對已刪除的元素所佔的空間進行回收。解決這個問題的方法是將所有未被分配的結點空間以及因刪除操作而回收的結點空間用遊標鏈成一個備用靜態鏈表。當進行插入操作時,先從備用鏈表上取一個分量來存放待插入的元素,然後將其插入到已用鏈表的相應位置。當進行刪除操作時,則將被刪除的結點空間鏈接到備用鏈表上以備後用。這種方法是指在已申請的大的存儲空間中有一個已用的靜態單鏈表,還有一個備用單鏈表。已用靜態單鏈表的頭指針爲1.備用靜態單鏈表的頭指針需另設一個變量av來表示。
靜態單鏈表的幾個基本操作算法:
space爲一維數組名
這裏的下標表示的就是數組的下標
下列三個操作的只有space備用鏈表,那麼怎麼訪問已用鏈表內的數據呢?space[0].cur指向的是空閒的結點呀。後面的difference例子,另利用了一個S來做已用鏈表的頭指針。

int LocateElem_SL(SLinkList space[])
{ //在靜態單鏈線性表L中查找第一個值爲e的元素。若找到,則返回它在L中的位序,否則//返回0
    i = S[0].cur;
    while(i && S[i].data != e)
        i = S[i].cur;
    return i;
}

void initSpace_SL(SLinkList space[])
{ //將一維數組space中各分量鏈成一個備用鏈表,space[0].cur爲頭指針,”0”表示空指針
    for(i=0; i < Maxsize-1; ++i )
        space[i].cur = i + 1;
        space[Maxsize-1].cur = 0;
}

int Malloc_SL(SLinkList space[])
{ //若備用空間鏈表非空,則返回分配的結點下標,否則返回0, 結果是將備用鏈表的頭指針之後的開頭第一個元素分配出去
    i = space[0].cur;
    if(space[0].cur)
        space[0].cur = space[i].cur; //備用鏈表的頭指針指向的第一個元素後移一個位置
    return i;
}

void Free_SL(SLinkList space[], int k)
{ //將下標爲k的空閒結點回收到備用鏈表,這個元素位於頭指針之後的第一個位置上
   space[k].cur = space[0].cur;
   space[0].cur = k;
}
void difference(SLinkList space[], int & S)   //這個暫時是算法,以後改成實碼
{ //依次輸入集合A和B的元素,在一維數組space中建立表示集合(A-B)and(B-A)
 //的靜態鏈表,S爲其頭指針。假設備用空間足夠大,space[0].cur爲其頭指針
//這裏S和space[0].cur兩個頭指針的區別?一個是已用鏈表一個是靜態鏈表?是的,s指向
//的就是第一個可用的元素,注意,上面的分配和刪除都是在鏈表的開始第一個結點上,但是
//到了添加結點到S所指向的可用鏈表上時,添加操作的位置是最後。
//in all,關於備用鏈表的操作位置在備用鏈表的開頭上,關於已用鏈表的操作,位置在已用
//鏈表的末尾上。
     InitSpace_SL(space);  // 初始化備用空間
     S = Malloc_SL(space); // 生成S的頭結點,S的cur指向的下一個纔是真正開始的data
     r = S;        // r指向S的當前最後結點
     scanf(m,n);  // 輸入A和B的元素個數
     for(j = 1; j <= m; ++j)   //建立集合A的鏈表
     {
         i = Malloc_SL(space); //分配結點
         scanf(space[i].data);  //輸入A的元素值
         space[r].cur = i;   //插入到表尾
         r = i;
}
space[r].cur = 0;
for(j = 1; j <= n; ++j)   //依次輸入b的值,若不在A中,則插入,否則刪除
{
    scanf(b);
    p = S;
    k = space[S].cur;   //k指向集合A中第一個結點, p在k的前面一個
    while(k != space[r].cur && space[k].data != b)  //在當前表中查找
    {
        p = k;
        k = space[k].cur;
}
if( k == space[r].cur) //當前表中不存在此元素,插入在r所指結點之後,且r的
//位置不變,因爲r標識的是元素A,即查詢的結束。那麼若B中的元素是重的呢?
//即B中有兩個相同的元素,A中也有,B的元素第一次出現,就把A中的相應元
//素刪了,   這種情況不會出現,因爲集合是有互異性的,不能有相同的元素。
{
    i = Malloc_SL(space);
    space[i].data = b;
    space[i].cur = space[r].cur;   // == 0
    space[r].cur = i;      //r的後面就是i
}
else    //該元素已在表中,刪除之
{  //此時k就是要刪除的這個結點,p是k之前的結點
     space[p].cur = space[k].cur;
     Free_SL(space, k);
     if(r == k) r = p;    //若刪除的是r所指結點,則需修改尾指針-----這個自己
//寫肯定想不到
}
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章