線性錶鏈表表相關習題及詳解(綜合) ——數據結構


習題部分

第一題

設計一個遞歸算法,刪除不帶頭結點的單鏈表L中所有值爲x的結點

第二題

在帶頭結點的單鏈表L中,刪除所有值爲x的結點,並釋放空間,假設值爲x的結點不唯一,試編寫算法以實現上述操作。

第三題

設L爲帶頭結點的單鏈表,編寫算法實現從尾到頭反向輸出每個結點的值

第四題

試編寫在帶頭結點的單鏈表L中刪除一個最小值結點的高效算法(假設最小值結點時唯一的)

第五題

試編寫算法將帶頭結點的單鏈表就地逆置,所謂“就地”是指輔助空間複雜度爲O(1)。

第六題

有一個帶頭結點的單鏈表L,設計一個算法使其元素遞增有序。

第七題

設在一個帶表投機誒單的單鏈表中所有元素結點的數據值無序,試編寫一個函數,刪除表中所有介於給定的兩個值(作爲函數參數給出)之間的元素的元素(若存在)

第八題

給定兩個單鏈表,編寫算法找出兩個鏈表的公共結點。

第九題

給定一個帶表頭結點的單鏈表,設head爲頭指針,結點的結構爲(data,next),data爲整形元素,next爲指針,試寫出算法:按遞增次序輸出單鏈表中各結點的數據元素,並釋放結點所佔的存儲空間(要走:不允許使用數組作爲輔助空間)

第十題

給定一個帶表頭結點的單鏈表A分解爲兩個帶頭結點的單鏈表A和B,使得A表中含有原表中序號爲奇數的元素,而B表中含有原表中序號爲偶數的元素,且保持其相對順序不變

第十一題

設C={a1,b1,a2,b2…,an,bn}位線性表,採用帶頭結點的hc單鏈表存放,設計一個就地算法,將其拆分爲兩個線性表,使得

A={a1,a2,…,an} B={bn,…,b2,b1}

第十二題

在一個遞增有序的線性表中,有數值想通的元素存在。若存儲方式爲單鏈表,設計算法去掉數值相同的元素,使表中不再有重複的元素。例如(7,10,10,21,30,42,42,42,51,70)將變作(7,10,21,30,42,51,70)

第十三題

假設有兩個按元素值遞增次序排列的線性表,均以單鏈表形式存儲。請編寫算法將這兩個單鏈表歸併爲一個按元素值遞減次序排列的單鏈表,並要求利用原來兩個單鏈表的結點存放歸併後的單鏈表

第十四題

設A和B是兩個單鏈表(帶頭結點),其中元素遞增有序,設計一個算法從A和B中公共元素產生單鏈表C,要求不破壞A B 的結點

第十五題

一直兩個鏈表A和B分別表示兩個集合,其元素遞增排列。繪製函數,求A與B的交集,並存放於A鏈表中。

第十六題

兩個整數序列A=a1,a2,a3,…,am和B=b1,b2,b3,…,bn已經存入兩個單鏈表中,設計一個算法,判斷序列B是否是序列A的連續子序列

第十七題

設計一個算法用於判斷帶頭結點的循環雙鏈表是否對稱

第十八題

有兩個循環單鏈表,鏈表頭指針分別爲h1和h2,編寫一個函數將鏈表h2鏈接到鏈表h1之後,要求鏈接後的鏈表仍保持循環鏈表形式。

第十九題

設有一個帶頭結點的循環單鏈表,其結點值均爲正整數。設計一個算法,反覆找出單鏈表中結點值最小的結點並輸出,然後將該結點從中刪除,直到單鏈表爲空位置,再刪除表頭結點

第二十題

設頭指針爲L的帶有表頭節點的非循環雙向鏈表,其每個結點中除了有pred(前去指針),data(數據)和next(後繼指針)外,還有一個訪問頻度域freq。在鏈表被啓用前,氣質均初始化爲零。每當在鏈表中進行一次Locate(L,x)運算時,令元素值爲x的結點中freq域的值增加1,並使此鏈表中結點保持按訪問頻度非增(遞減)的順序排列,同事最近訪問的結點排在頻度相同的結點的前面,以便使頻繁訪問的結點總是靠近表頭。試編寫符合上述要求的Locate(L,x)運算的算法,該運算爲函數過程,返回找到結點的地址,類型爲指針型


解答部分

第一題

設f(L,x)的功能是刪除以L爲首結點指針的單鏈表中所有值等於x的結點,則顯然有f(L->next,x)的功能是刪除以L->next爲首結點指針的單鏈表中所有值等於x的結點,由此,可以退出遞歸模型如下:

終止條件:f(L,x) 不作任何事情; 若L爲空表
遞歸主體:f(L,x) 刪除*L結點;f(L->next,x); 若L->data==x
                 f(L,x) f(L->next,x); 其他情況

本題代碼如下:

void Del_X_3(Linklist &L,ElemType x){
    LNode *p;               //p指向待刪除結點
    if (L==NULL)            //遞歸出口
    {
        return;
    }
    if(L->data == x){       //若L所指結點的值爲x
        p=L;                //刪除*L,並讓L指向下一結點
        L=L->next;
        free(p);
        Del_X_3(L,x);       //遞歸調用

    }
    else                    //若L所指結點的值不爲x
        Del_X_3(L->next,x); //遞歸調用
}

算法需要藉助一個遞歸工作棧,深度爲O(n),時間複雜度爲O(n)。有人認爲直接free屌結點會造成斷鏈,實際上因爲L爲引用,是直接對原鏈表進行操作,因此不會斷鏈。

第二題

解法一

用p從頭到尾掃描單鏈表,pre指向*p結點的前驅。若p所指結點的值爲x,則刪除,並讓p移向下一個結點,否則讓pre、p指向同步後移一個結點。

void Del_X_1(Linklist &L,ElemType x){
    //L爲帶頭結點的但倆你報,本算法刪除L中所有值爲x的結點
    LNode *p=L->next,*pre=L,*q;//置p和pre的初始值
    while(p!=NULL){
        if(p->data==x){
            q=p;                //q指向該結點
            p=p->next;
            pre->next=p;        //刪除*q結點
            free(q);            //釋放*q結點的空間
        }
        else{                   //柔則,pre和p同步後移
            pre=p;  
            p=p->next;
        }//else
    }//while
}

本算法是在無須單鏈表中刪除滿足某種太哦啊見的所有結點,這裏的條件是結點的值爲x。實際上,這個條件是可以任意指定的,只要修改if條件即可,比如,我們要求刪除值介於mink和maxk之間的所有結點,則只需將if語句修改爲ifp->datamink && p->data

void Del_X_2(Linklist &L,ElemType x){
    //L爲帶頭結點的但倆你報,本算法刪除L中所有值爲x的結點
    LNode *p=L->next,*r=L,*q;       //r指向尾結點,其初值爲頭結點
    while(p!=NULL){
        if(p->data!=x){             //*p結點值不爲x時將其鏈接到L尾部
            r->next=p;
            r=p;
            p=p->next;              //繼續掃描
        }
        else{                       //*p結點值爲x時將其釋放
            q=p;
            p=p->next;              //繼續掃描
            free(q);                //釋放空間
        }
    }//while
    r->next=NULL;                   //插入結束後置尾結點指針爲NULL
}

第三題

考慮到從頭到尾輸出比較簡單,本題的思路很自然的聯繫到藉助上題鏈表逆置的方法來實現,改變鏈表的方向,然後就可以從頭到尾實現反向輸出了

此外,本題還可以藉助一個棧來實現,每經過一個結點時,將該結點訪入棧中。在遍歷完整個鏈表後,再從棧頂開始輸出結點值即可。

既然能用棧的思想解決,也就很自然地聯想到了用遞歸來實現,每當訪問一個結點時,先遞歸輸出它後面的結點,再輸出該結點自身,這樣鏈表就反向輸出了。

本體代碼如下:

void R_Print(LinkList L){
    //從尾到頭輸出單鏈表L中每個結點的值
    if(L->next!=NULL){
        R_Print(L->next);   //遞歸
    }//if
    printf(L->data);        //輸出函數


}

第四題

算法思想:用p從頭至尾掃描單鏈表,pre指向*p結點的前驅,用minp保存值最小的結點指針(初值爲p),minpre指向*minp結點的前驅(初值爲pre)。一邊掃描,一邊比較,若p->data小於minp->dara,則將p、pre分別複製給minp、minpre。

當p掃描完畢,minp指向最小值結點,minpre指向最小值結點的前驅結點,再將minp所指結點刪除即可。

LinkList Delete_Min(LinkList &L){
    //L是帶頭結點的單鏈表,本算法刪除其最小值結點
    LNode *pre=L,*p=pre->next;      //p爲工作指針,pre指向其前驅
    LNode *minpre=pre,*minp =p;     //保存最小值結點及其前驅
    while(p!=NULL){
        if(p->data < minp->data){
            minp=p;                 //找到比之前找到的最小值結點更小的結點
            minpre=pre;
        }
        pre=p;                      //繼續掃描下一個結點
        p=p->next;
    }
    minpre->next=minp->next;        //刪除最小值結點
    free(minp);
    return L;
}

算法需要從頭至尾掃描鏈表,時間複雜度爲O(n),空間複雜度爲O(1)。

如果本題改爲不帶頭結點的單鏈表,則實現上會有所不同

第五題

解法一

將頭結點摘下,然後從第一結點開始,一次前插入到頭結點的後面(頭插法建立單鏈表),直到最後一個結點爲止,則實現了鏈表的逆置,

LinkList Reverse_1(LinkList L){
    //L是帶頭結點的單鏈表,本算法將L就地逆置
    LNode *p ,*r;               //p爲工作指針,r爲p的後繼,以防斷鏈
    p-L->next;                  //從第一個元素結點開始
    L->next=NULL;               //先將頭結點L的next域置爲NULL
    while(p!=NULL){             //依次將元素結點摘下
        r=p->next;              //暫存p的後繼
        p->next=L->next;        //將p結點插入到頭結點之後
        L->next=p;              //
        p=r;                    //
    }
    return L;
}

解法二

大部分數據結構只介紹第一種方法。下面假設pre、p和r指向3個相鄰的結點,如下圖所示。假設經過若干操作,*pre之前的結點的指針都已調整完畢,它們的next都指向其原前驅結點。現在令 *p結點的next域指向*pre結點,注意到一旦調整指針的指向後,*p的後繼結點的鏈就斷開了,爲此需要用r來指向原*p的後繼結點。處理時需要注意兩點:一是在處理第一個結點時,應將其next域置爲NULL,而不是指向頭結點(因爲它將作爲新表的尾結點);二十在處理完最後一個結點後,需要將頭結點的指針指向它。

本題代碼

LinkList Reverse_2(LinkList L){
    //依次遍歷線性表L,並將結點指針翻轉
    LNode *pre,*p=L->next,*r=p->next;
    p->next =NULL;          //處理第一個結點
    while(r!=NULL){         //r爲空,則說明p爲最後一個結點
        pre=p;              //依次繼續遍歷
        p=r;
        r=r->next;
        p->next=pre;        //指針反轉
    }
    L->next=p;              //處理最後一個結點
    return L;
}

上述兩個算法的時間複雜度爲O(n),空間複雜度爲O(1)

第六題

算法思想:採用直接插入排序算法的思想,先構成只含一個數據結點的有序單鏈表,然後依次掃描單鏈表中剩下的結點*p(直至p==NULL爲止),在有序表中通過比較查找插入*p的前驅結點*pre,然後*p插入到*pre之後,找到合適的位置插入

本題代碼如下:

void Sort(LinkList &L){
    //本算法實現將單鏈表L的結點重拍,使其遞增有序
    LNode *p=L->next,*pre;
    LNode *r=p->next;                   //r保持*p後繼結點指針,以保證不鍛鍊
    p->next=NULL;                       //構造只含一個數據結點的有序表
    p=r;
    while(p!=NULL){
        r=p->next;                      //保存*p的後繼結點指針
        pre=L;
        while(pre->next!=NULL&&pre->next->data < p->data){
            pre=pre->next;              //在有序表中查找插入*p的前驅結點*pre
        }
        p->next=pre->next;              //將*p插入到*pre後
        pre->next=p;
        p=r;                            //掃描原單鏈表中剩下的結點
    }
}

細心的話話顯然該算法的時間複雜度爲O(n^2)

第七題

因爲鏈表是無序的,所以只能逐個節點進行檢查,執行刪除。

void RangeDelete(LinkList &L,int min,int max){
    LNode *pr=L,*p=L->link;                 //p是檢測指針,pr是其前驅
    while(p!=NULL){
        if(p->data > min&&p->data < max){   //尋找到被刪結點,刪除
            pr->link = p->link;
            free(p);
            p=pr->link;
        }
        else{                               //否則繼續尋找被刪結點
            pr=p;
            p=p->link;
        }
    }
}

第八題

兩個單鏈表有公共結點,也就是說兩個鏈表從某一結點開始,它們的next都指向同一個結點。由於每個單鏈表結點只有一個next域,因此從第一個公共結點開始,之後它們所有的結點都是重合的,不可能再出現分叉。所以,兩個有公共結點而部分重合的單鏈表,拓撲形狀看起來像Y而不可能像X。

本題極容易聯想到“蠻力”的方法:在第一個鏈表上順序遍歷每個節點,每遍歷一個結點,在第二個鏈表上順序遍歷所有結點,如果找到兩個相同的結點,於是就找到了它們的公共結點。顯然該算法的時間複雜度爲O(len1*len2)。

接下來我們試着去尋找一個線性時間複雜度的算法。先把問題簡化:如何判斷兩個單向鏈表有沒有公共結點?應注意到這樣一個事實:如果兩個鏈表有一個公共結點,那麼該公共結點之後的所有結點都是重合的,即它們的最後一個結點必然是重合的。因此,我們判斷兩個鏈表是不是有重合的部分,只要分別遍歷兩個鏈表到最後一個結點。如果兩個尾結點是一樣的,說明它們有公共結點,否則兩個鏈表沒有公共的結點。

然而,在上面的思路中,順序遍歷兩個鏈到尾結點的時候,並不能保證兩個鏈表上同事到達尾結點。這是因爲兩個鏈表長度不一定一樣。但假設一個鏈表比另一個長k個結點,我們先在長的鏈表上遍歷k個結點,之後再同步遍歷,此時我們就能保證同時到達最後一個結點了。由於兩個鏈表從第一個公共結點開始到鏈表的尾結點,這一部分是重合的。因此它們肯定也是同時到達第一公共結點的。於是在遍歷中,第一個相同的結點就是第一個公共的結點。

在這個思路中,我們先要分別遍歷兩個鏈表得到它們的長度,並求出兩個長度之差。在長的鏈表上先遍歷長度之差個結點之後,再同步遍歷兩個鏈表,直到找到相同的結點,或者一直到鏈表結束。此時該方法的時間複雜度爲O(len1+len2)。

本題代碼如下

LinkList Search_1st_Common(LinkList L1, LinkList L2){
    //本算法實現在線性的時間內找到兩個單鏈表的第一個公共結點
    int len1 = Length(L1),len2=Length(L2);              //計算兩個鏈表的表長
    LinkList longList ,shortList;                       //分別指向表長較長和較短的鏈表
    if(len1>len2){                                      //L1表長較長
        longList = L1->next;shortList=L2->next;
        dist = len1-len2;                               //表長之差
    }
    else{                                               //L2表長較長
        longList = L2->next;shortList=L1->next;
        dist = len2-len1;                               //表長之差
    }
    while(dist--)                                       //表長的鏈表先遍歷到第dist個結點,然後同步
        longList =longList->next;
    while(longList!=NULL){                              //同步尋找共同結點
        if(longList==shortList)                         //找到第一個公共結點
            return longList;
        else{                                           //繼續同步尋找
            longList=longList->next;
            shortList=shortList->next;
        }
    }//while
    return NULL;

}

第九題

算法思想:對鏈表進行遍歷,在每趟遍歷中查找出整個鏈表的最小值元素,輸出並釋放結點所佔空間;再查找次小值元素,輸出並釋放空間,如此下去,直至鏈表爲空,最後釋放頭結點所佔存儲空間,該算法的時間複雜度爲O(n^2)。

若題設不限制數組輔助空間的使用,則可先將鏈表的數據複製在數組裏,再採用時間複雜度爲O(nlog2n)的排序算法進行派速,然後將數組元素輸出,時間複雜度爲O(nlog2n)

第十題

算法思想:設置一個訪問序號變量(初值爲0),每訪問一個結點序號自動加1,然後根據序號的奇偶性將結點插入到A表或B表中。重複以上操作直到表尾

void Min_Delete(LinkList &head){
    //head是帶頭結點的單鏈表的頭指針,本算法按遞增順序輸出單鏈表中的數據元素
    while(head->next!NULL){                     //循環到僅剩頭結點
        pre=head;                               //pre爲元素最小值結點的寢取結點和指針
        p=pre->next;                            //p爲工作指針
        while(p->next!=NULL){
            if(p->next->data < pre->next->data)
                pre=p;                          //記住當前最小值結點的前驅
            p=p->next;
        }
        print(pre->next->data);                 //輸出元素最小值結點的數據
        u=pre->next;                            //刪除元素值最小的結點,釋放結點空間
        pre->next=u->next;
        free(u);

    }//while
    free(head);                                 //釋放頭結點
}

爲了保持原來結點中的順序,本題採用尾插法建立單鏈表。此外,本算法完全可以不用設置序號變量。while循環中的代碼改爲將結點插入到表A中和將下一結點插入到表B中,這樣while中第一處理的結點就是奇數號結點,第二處理的結點就是偶數號結點。

第十一題

算法思想:採用上面的思路,不設序號變量。二者的差別僅在於對B表的建立不採用尾插法,而是採用頭插法。

LinkList DisCreat(LinkList &A){
    LinkList B= (LinkList)malloc(sizeof(LNode));    //創建B表表頭
    B->next=NULL;                                   //B表的初始化
    LNode *p=A->next,*q;                            //p爲工作指針
    LNode *ra=A;                                    //ra始終指向A的尾結點
    while(p!=NULL){
        ra->next=p;ra=p;                            //將*p鏈到A的表尾
        p=p->next;
        q=p->next;                                  //頭插後,*p將斷鏈,因此用q記憶*p的後繼
        p->next=B->next;                            //將*p插入到B的前端
        B->next=p;
        p=q;
    }
    ra->next=NULL;                                  //A尾結點的next域置空
    return B;
}

該算法特別需要注意的是,採用頭插法插入結點後,*p的指針域已經改變,如果不設變量保存其後繼結點會引起斷鏈,從而導致算法出錯。

第十二題

算法思想:由於是有序表,所有相同值域的結點都是相鄰的。用p掃描遞增單鏈表L,若*p結點的值域等於其後繼結點的值域,則刪除後者,否則p移向下一個結點

void Del_Same(LinkList &L){
    //L是遞增有序的單鏈表,本算法刪除表中數值相同的元素
    LNode *p=L->next,*q;            //p爲掃描工作指針
    if(p==NULL)
        return;
    while(p->next!=NULL){
        q=p->next;                  //q指向*p的後幾及誒單
        if(p->data==q->data){       //找到重複值的結點
            p->next=q->next;        //釋放*q結點
            free(q);                //釋放相同元素值的結點
        }
        else
            p=p->next;
    }
}

本題算法的時間複雜度爲O(n),空間複雜度爲O(1)。
本題也可以採用尾插法,將頭結點摘下,然後從第一結點開始,一次與已經插入結點的鏈表的最後一個結點比較,若不等則直接插入,否則將當前遍歷的結點刪除並處理下一個結點,直到最後一個結點爲止。

第十三題

算法思想:兩個鏈表已經按元素值遞增次序排序,將其合併時,均從第一個結點起進行比較,將笑的結點鏈入鏈表,同時後移工作指針。該問題要求結果倆你報按元素值遞減次序,故新鏈表的建立應該採用頭插法。比較結束後,可能會有一個鏈表非空,此時用頭插法將剩下的結點一次插入新鏈表中即可。。。

void MergeList(LinkList &La,LinkList &Lb){
    //合併兩個遞增有序鏈表(帶頭結點),並使合併後的鏈表遞增排列
    LNode *r,*pa=La->next,*pb=Lb->next; //分別是表La和Lb的工作指針
    La->next=NULL;                      //La作爲結果鏈表的頭指針,先將結果鏈表初始化爲空

    while(pa&&pb){                      //當兩鏈表均不爲空時,循環
        if(pa->data<=pb->data){
            r=pa->next;                 //r暫存pa的後繼結點指針
            pa->next=La->next;
            La->next=pa;                //將pa結點鏈於結果表中,同事逆置(頭插法)

            pa=r;                       //恢復pa爲當前待比較結點
        }
        else{
            r=pb->next;                 //r暫存pb的後繼結點指針
            pb->next=La->next;
            La->next=pb;                //將pb結點鏈於結果表中,同事逆置(頭插法)

            pb=r;                       //恢復pb爲當前待比較結點
        }
    }
    if(pa)                              //通常情況下回剩一個鏈表非空,處理剩下的部分
        pb=pa;
    while(pb){                          //處理剩下的一個非空鏈表
        r=pb->next;                     //一次插入到La中(頭插法)
        pb->next=La->next;
        La->next=pb;
        pb=r;
    }
    free(Lb);
}

第十四題

算法思想:表A ,B都有序,可從第一個元素起一次比較A、B量表的元素,若元素值不等,則值小的指針往後移,若元素值相等,則創建一個值等於兩結點的元素值的新的結點,使用尾插法插入到新的鏈表中,並兩個原表指針後移一位,直到其中一個鏈表遍歷到表尾。

void Get_Common(LinkList A, LinkList B){
    //本算法產生單鏈表A和B的公共元素的單鏈表的C
    LNode *p=A->next,*q-B->next,*r,*s;
    LinkList C=(LinkList)malloc(sizeof(LNode)); //建立表C
    r=C;                                        //r始終指向C的尾結點
    while(p!=NULL&&q!=NULL){                    //循環挑出條件
        if(p->data<q->data)
            p=p->next;                          //若A的當前元素較小,後移指針
        else if(p->data>q->data)
            q=q->next;                          //若B的當前元素較小,後移指針
        else{                                   //找到公共元素結點
            s=(LNode*)malloc(sizeof(LNode));
            s->data=p->data;                    //複製產生結點*s
            r->next=s;                          //將*s鏈接到C上(尾插法)
            r=s;
            p=p->next;                          //表A和表B繼續向後掃描
            q=q->next;
        }
    }
    r->next=NULL;                               //置C尾結點指針爲空
}

第十五題

算法思想:採用歸併的思想,設置兩個工作指針pa和pb,對兩個鏈表進行歸併掃描,只有同時出現在兩集合中的元素才鏈接到結果表中且僅保留一個,其他的結點全部釋放。當一個鏈表遍歷完畢後,釋放另一個表中剩下的全部結點。。

LinkList Union(LinkList &la,LinkList &lb){
    pa=la->next;                        //設工作指針分別爲pa和pb
    pb=lb->next;
    pc=la;                              //結果表中當前合併結點的前去指針
    while(pa&&pa){
        if(pa->data==pb->data){         //交集併入結果表中
            pc->next=pa;                //A中結點鏈接到結果表
            pc=pa;
            pa=pa->next;
            u=pb;                       //B中結點釋放
            pb=pb->next;
            free(u);
        }
        else if(pa->data<pb->data){     //若A中當前結點值小於B中當前結點值
            u=pa;
            pa=pa->next;                //後移指針
            free(u);                    //釋放A中當前結點
        }
        else{                           //若B中當前結點值小於A中當前結點值
            u=pb;
            pb=pb->next;                //後移指針
            free(u);                    //釋放B中當前結點
        }
    }//while結束
    while(pa){                          //B已經遍歷完,A沒完
        u=pa;
        pa=pa->next;
        free(u);                        //釋放A中剩餘節點
    }
    while(pb){                          //A已經遍歷完,B未完
        u=pb;
        pb=pb->next;
        free(u);                        //釋放B中剩餘結點
    }
    pc->next=NULL;                      //置結果鏈表尾指針爲NULL
    free(lb);                           //釋放B表的頭結點
    return la;
}

該算法的時間複雜度爲O(len1+len2),空間複雜度爲O(1)

第十六題

算法思想:因爲兩個整數序列已存放如兩個鏈表中,操作從兩個鏈表的第一個結點開始,若對應數據相等,則後移指指針;若對應數據不等,則A倆你報從上次開始比較結點的後繼開始,鏈表扔從第一個結點開始比較,直到B鏈表到尾表示匹配成功。A鏈表到尾而B連不了未到尾表示失敗。操作中應記住A鏈表每次的開始結點,以便下趟匹配時剛好從其後繼開始。

int Pattern(LinkList A,LinkList B){
    //A和B分別是數據域爲整數的單鏈表,本算法判斷B是否是A的子序列
    LNode *p=A;                 //p爲A鏈表的工作指針,本題假設A和B均無頭結點
    LNode *pre=p;               //pre記住每趟比較中A鏈表的開始結點
    LNode *q=B;                 //q是B鏈表的工作指針
    while(p&&q){
        if(p->data==q->data){   //結點值相同
            p=p->next;
            q=q->next;
        }
        else{
            pre=pre->next;
            p=pre;              //A鏈表新的開始比較結點
            q=B;                //q從B鏈表第一個結點開始
        }

    }
    if(q==NULL)                 //B已經比較結束
        return 1;               //說明B是A的子序列
    else
        return 0;               //B不是A的子序列
}

此題其實是字符串模式匹配的鏈式表示形式。

第十七題

算法思想:讓p從左向右掃描,q從右向左掃描,直到它們指向同一結點(p==q,當循環雙鏈表中節點個數爲奇數時)活相鄰(p->next=q或p->prior=p,當循環雙鏈表中結合個數爲偶數時)未知,若它們所指結點值相同,則繼續進行下去,否則返回0。若比較全部黨性,則返回1。

int Symmetry(DLinkList L){
    //本算法從兩頭掃描循環雙鏈表,以判斷鏈表是否對稱
    DNode *p = L->next,*q=L->prior;     //兩頭工作指針
    while(p!=q&&q->next!=p){            //循環挑出條件
        if(p->data==q->data){           //所指結點值相同則繼續比較
            p=p->next;
            q=q->prior;
        }
        else                            //否則,返回0
            return 0;
    }
    return 1;                           //比較結束後返回1
}

第十八題

算法思想:先找到兩個鏈表的尾指針,將第一個鏈表的尾指針與第二個鏈表的頭結點鏈接起來,在使之成爲循環的。

···
LinkList Link(LinkList &h1,LinkList &h2){
//將循環鏈表h2鏈接到循環鏈表h1之後,使之仍保持循環鏈比老的形式
LNode *p,*q; //分別直線搞兩個鏈表的尾結點
p=h1;
while(p->next!=h1) //尋找h1的尾結點
p=p->next;
q=h2;
while(q->next!=h2) //尋找h2的尾結點
q=q->next;
p->next =h2; //將h2鏈接到h1之後
q->next=h1; //令h2的尾結點指向h1
return h1;
}
···

第十九題

對於循環但倆你報L,在不空時循環:每循環一次查找一個最小結點(由minp指向最小值結點,minpre指向其前驅結點)並刪除它,最後釋放頭結點

void Del_All(LinkList &L){
    //本算法實現每次刪除循環單鏈表中的最小元素,知道鏈表爲空爲止
    LNode *p, *pre,*minp, *minpre;
    while(L->next!=L){                      //表不空,循環
        p=L-next;pre=L;                     //p爲工作指針,pre指向其前驅
        minp=p;minpre=pre;                  //minp指向最小值結點
        while(p!=L){                        //循環一趟,查找最小值結點
            if(p->ndata < minp->data){
                minp =p;                    //找到值更小的結點
                minpre=pre;
            }
            pre=p;                          //查找下一個結點
            p=p-next;
        }
        printf("%d",minp->data)             //輸出最小值結點元素
        minpre->next=minp->next;            //最小值結點從表中“斷”開
        free(minp);                         //釋放空間
    }
    free(L);                                //釋放頭結點
}

第二十題

此題主要考察雙鏈表的查找、刪除和插入算法。
算法思想:首先在雙向鏈表中查找數據值爲x的結點,查到後,將結點從鏈表上摘下,然後再順着結點的前驅鏈查找該結點的插入位置。(頻度遞減,且排在同頻度的第一個,即向前找到第一個比它頻度大的結點,插入位置爲該結點之後),並插入到該位置。

DLinkList Locate(DLinkList &L, ElemType x){
    //本算法先查找數據x,查找成功時結點的訪問頻度域增加1
    //最後將該結點按頻度遞減插入鏈表中適當位置(同頻度最近訪問的在前邊)
    DNode *p=L->next, *q;               //p爲工作指針,q爲p的前驅,用於查找插入位置
    while(p&&p->data!=x)
        p=p->next;                      //查找值爲x的結點
    if(!p){
        printf("不存在值爲x的結點\n" );
        exit(0);
    }
    else{                               //令元素值爲x的結點的freq域加1
        p->freq++;
        p->next->pred=p->pred;          //將p結點從鏈表上摘下
        p->pred->next=p->next;          //以下查找p結點的插入位置
        q=p->pred;
        while(q!=L&&q->freq<=p->freq)
            q=q->pred;
        p->next=q->next;
        q->next->pred=p;                //將p結點插入,一定是排在同頻率的第一個
        p->pred=q;
        q->next=p;
    }
    return p;                           //返回值爲x的結點的指針
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章