線性表學習筆記之鏈表


原創博文,轉載請注明出處

鏈表分類:單鏈表,插入刪除和查找的時間復雜度均為O(n)

              雙鏈表,插入、刪除和查找的時間復雜度為O(1)

              循環鏈表,表中最后一個節點的指針不是NULL,而改為指向頭結點,從而整個鏈表形成一個環。

             靜態鏈表,借助數組來描述線性表的鏈式存儲結構,這兒的指針是結點的相對地址。和順序表一樣需要預先分配一塊連續的內存空間。以next==0作為其結束的標志。

綜合應用:

    1.設計一個遞歸算法,刪除不帶頭節點的單鏈表L中所有值為x的節點。

            思路:可以設計一個函數f(L,x)刪除以L為首結點指針的單鏈表中所有值為x的結點,那么f(L->next,x)則是刪除以L->next為首結點指針的單鏈表中所有值等於x的結點。

                    借助一個遞歸工作棧,深度為O(n),時間復雜度為O(n)

 1 void Del_x(Linklist &L, ElemType x){
 2       LNode *p;   //p指向待刪除結點
 3       
 4       if(L==NULL)
 5             return;
 6       if(L->data==x){
 7             p=L;
 8             L=L->next;
 9             free(p);
10             Del_x(L, x);    
11     }           
12      else
13            Del_x(L->next, x);       
14 }
View Code

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

           思路:方法一、將鏈表逆置,改變鏈表的方向。

                  方法二、借助一個棧,將結點放入棧中。在遍歷完整個鏈表后,再從棧頂開始輸出結點值。

                  方法三、使用遞歸,當訪問一個結點時,先遞歸輸出它后面的結點,再輸出該結點自身。實現如下

1 void R_Print(LinkList L){
2         if(L->next!=NULL){
3               R_Print(L->next)
4     }
5        print(L->next)
6 }
View Code

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

        

 1 LinkList Delete_Min(LinkList &L){
 2          LNode *pre=L, *p=L->next; //p為工作指針,pre指向其前驅
 3          LNode *minpre=pre, *minp=p;
 4         
 5          while(p!=NULL){
 6                  if(p->data<minpre->data){
 7                           minp=p;
 8                           minpre=pre;
 9         }  
10                pre=p;
11                p=p->next;
12     }
13     minpre->next=minp->next;
14     free(minp);
15     return L;
16 }    
View Code

        4.編寫算法將帶頭結點的單鏈表就地逆置,就地指輔助空間為O(1)

         方法一:將頭結點摘下,然后從第一結點開始,依次前插入到頭節點后面(頭插法建立鏈表),直到最后一個節點為止。

 1 LinkList Reverse(LinkList &L){
 2            p=L->next;
 3            L->next=NULL;
 4            while(p!=NULL){
 5                   r=p->next;
 6                   p->next=L->next;
 7                   L->next=p;
 8                   p=r;
 9      }
10             return L;
11 }
View Code

        方法二:將結點的next域指向前驅結點。在處理完最后一個結點時,需要將頭結點的指針指向它。時間復雜度均為O(n)

 1 LinkList Reverse(LinkList &L){
 2              LNode *pre,*p=L->next,*r=p->next;
 3              p->next=NULL;
 4              while(r!=NULL){
 5              pre=p;
 6              p=r;
 7              r=r->next;
 8              p->next=pre;
 9     }
10             L->next=p;
11             return L;
12 }
View Code

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

      思路:采用直接插入排序的算法,先構成只含一個數據結點的有序單鏈表,然后依次掃描單鏈表中剩下的結點*p。

 

 1 void Sort(LinkList &L){
 2         LNode *p=L->next, *pre
 3         LNode *r=p->next;
 4         p->next=NULL;
 5         p=r;
 6         while(p!=NULL){
 7                 r=p->next;
 8                 pre=L;
 9                 while(pre->next!=NULL&&pre->next->data<p->data)
10                          pre=pre->next;
11                p->next=pre->next;
12                pre->next=p;
13                p=r;
14     }
15 
16 }
View Code

    6.在單鏈表L中刪除p所指結點,能夠實現在O(1)的時間內刪除該結點?

     思路:傳統的做法需要順序查找刪除結點的前驅結點,再修改鏈接。但是時間復雜度為O(n)。由於我們知道該結點的下一結點P->next,所以我們只需要將下一結點的數據復制到該結點,然后刪除它的下一結點。如果該結點位於鏈表尾部 即P=NULL,這時候我們需要從鏈表的頭結點開始順序遍歷給定節點的前驅結點,這時雖然時間復雜度為O(n),但在平均情況下,仍為O(1)。

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

    思路:比較麻煩的做法就是在第一個鏈表上順序遍歷每個節點,每遍歷一個結點都要在第二個鏈表上順序遍歷所有結點。找到兩個相同的結點,該算法時間復雜度為O(len1*len2)。

            由於每個單鏈表結點只有一個next域,因此從第一個公共結點開始,之后所有的結點都是重合的,拓撲形狀看起來像Y,而不是X。但是,兩個鏈表有可能不一樣長,所以我們需要截取長鏈表多余的部分。

 1 LinkList Search_Common(LinkList &L1,LinkList &L2){
 2            int len1=Length(L1),len2=Length(L2);
 3            LinkList longList,shorList;
 4           if(len1>len2){
 5                 longList=L1->next;shortList=L2->next;
 6                 dist=len1-len2;
 7     }
 8          else{
 9                 longList=L2->next,shortList=L1->next;
10                 dist=len2-len1;
11     }
12      while(dist--)
13                longList=longList->next
14     while(longList!=NULL){
15            if(longList==shortList)
16                   return longList;
17            else{
18             longList=longList->next;
19             shortList=shortList->next;
20          }
21     }
22      return NULL;
23 }
View Code

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

   思路:采用頭插法新建B表,而A表則使用尾插法。

 1 LinkList DisCreat(LinkList &A){
 2          LinkList B=(LinkList)malloc(sizeof(LNode))
 3          B->next=NULL;
 4          LNode *p=A->next;
 5          LNode *ra=A;   //ra始終指向A的尾結點
 6          while(p!=NULL){
 7                  ra->next=p;
 8                  ra=p;
 9                  p=p->next;
10                  q=p->next;
11                  p->next=B->next; 
12                  B->next=p;
13                  p=q;
14     }  
15         ra->next=NULL;
16         return B;
17 }
View Code

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

  思路:由於兩個鏈表均是遞增的,將其合並時,從第一個結點起進行比較,將小的結點存入鏈表中,同時后移工作指針。由於要求鏈表元素遞減,故采用頭插法。

 1 void MergeList(LinkList &A,LinkList &B){
 2             LNode *pa=A->next, *pb=B->next, *r;
 3             A->next=NULL;
 4             while(pa&&pb){
 5                      if(pa->data<pb->data){
 6                              r=pa->next;
 7                              pa->next=A->next;
 8                              A->next=pa; 
 9                              pa=r;
10                     }
11                    else{
12                             r=pb->next;
13                             pb->next=A->next;
14                             A->next=pb;
15                             pb=r;
16                     }
17           }
18           if(pa)
19              pb=pa;
20           while(pb){
21               r=pb->next;
22               pb->next=A->next;
23               A->next=pb;
24               pb=r;
25       }
26 }    
View Code

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

       思路:尾插法新建鏈表C ,要求不破壞A和B的結點,所以才有比較復制的方法。

 1 void Get_Common(LinkList &A , LinkList &B){
 2              LNode *p=A->next, *q=B->next, *r, *s;
 3             LinkList C =(LinkList)malloc(sizeof(LNode));
 4             r=C;
 5             while(p!=NULL&&q!=NULL){
 6                    if(p->data<q->data)
 7                         p=p->next;
 8                   else if(p->data>q->data)
 9                         q=q->next;
10                   else{
11                   s=(LNode *)malloc(sizeof(LNode));
12                   s->data=q->data;
13                   r->next=s;
14                   r=s;
15                  p=p->next;
16                  q=q->next;
17             }
18     }
19     r->next=NULL;
20 }
View Code

11. 已知兩個鏈表A和B分別表示兩個集合,其元素遞增有序。編制函數,求A與B的交集,並存放於A的鏈表中。

  思路:采用歸並思想,設置兩個工作指針pa和pb,對兩個鏈表進行掃描,當同時出現在兩集合中的元素才鏈接到結果表中,且僅保留一個,其他的結點全部釋放。

當一個鏈表遍歷結束后,釋放另一個表剩下的全部結點。

 1 LinkList Union(LinkList &A , LinkList &B){
 2           pa=A->next;
 3           pb=B->next;
 4           pc=A;
 5           LNode *u;
 6           while(pa&&pb){
 7                 if(pa->data==pb->data){
 8                        pc->next=pa;
 9                        pc=pa;
10                        pa=pa->next;
11                        u=pb;
12                        pb=pb->next;
13                        free(u);
14            }
15            else if(pa->data>pb->data){
16                     u=pb;
17                     pb=pb->next;
18                     free(u);
19          }
20          else{
21                  u=pa;
22                  pa=pa->next;
23                  free(u)
24         }
25         while(pa){
26                 u=pa
27                 pa=pa->next;
28                 free(u)
29          }
30         while(pb){
31                 u=pb
32                 pa=pb->next;
33                 free(u) 
34          }
35        pc->next-NULL;
36        free(B);
37        return A;
38     }
39 }
View Code

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

思路:讓P從左向右掃描,q從右向左掃描。直到它們指向同一結點(結點個數為奇數時)或相鄰(結點個數為偶數時)為止,若它們所指結點值相同,則繼續進行下去,否則返回0,若比較全部相同則返回1。

 

 1 int Symmetry(DLinkList L){
 2           DNode *p=L->next, *q=L->prior;
 3           while(p!=q&&p->next!=q)
 4                  if(p->data==q->data){
 5                      p=p->next;
 6                      q=q->prior;
 7                  }
 8                  else
 9                      return 0;
10           return 1;
11 }
View Code

13. 設有一個帶頭結點的循環單鏈表,其結點值均為正整數。設計一個算法,反復找出單鏈表中結點值最小的結點並輸出,然后將該結點從中刪除,直到單鏈表空為止,再刪除表頭結點。

 1 void Del_All(LinkList &L){
 2           LNode *p, *pre, *minp, *minpre;
 3           while(L->next!=L){
 4                  p=L->next;pre=L;
 5                  minp=p,minpre=L;
 6                  while(p!=L){
 7                        if(p->data<minp->data){minp=p;minpre=pre;}
 8                        pre=p;
 9                        p=p->next;
10                }
11                printf("%d",minp->data);
12                minpre->next=minp->next;
13                free(minp);
14                Del_All(L)
15     }
16          free(L);
17 }   
View Code

14. 已知一個帶有表頭結點的單鏈表,假設該鏈表只給出了頭指針list。在不改變鏈表的前提下,請設計一個盡可能高效的算法,查找鏈表中倒數第K個位置上的結點。若查找成功,算法輸出該結點的data域的值,否則,只返回0。

     設計思想:定義兩個指針變量p和q,初始時均指向頭結點的下一個結點,即鏈表的第一個結點。p指針沿鏈表移動;當p指針移動到第k個結點時,q指針開始於p指針同步移動;當p指針移動到最后一個結點時,q指針所指示的結點為倒數第k個結點。

 1 typedef int ElemType;
 2              typedef struct LNode{
 3                    ElemType data;
 4                    struct LNode *next;
 5     }LNode, *LinkList;
 6 
 7 int Search(LinkList L, int k){
 8            LNode *p=L->next, *q=L->next;
 9            int count=0;
10            while(p!=NULL){
11                  if(count<k)count ++;
12                  else q=q->next;
13                  p=p->next;
14     }
15        if(count<k)
16                return 017        else{
18         printf("%d",q->data);
19         return 1;
20     }
21 
22 }
View Code

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

   設計思想: 首先找到鏈表中數據值為x的結點,查到后,將結點從鏈表上摘下,然后順着結點的前驅查到該結點的插入位置。頻度遞減,且排在同頻度的第一個。

 1 DLinkList Locate(DLinkList &L, ElemType x){
 2           DNode *p=L->next, *q;
 3           while(p&&p->data!=x)           
 4                  p=p->next;
 5           if(!p){
 6               printf("不存在值為x的結點\n");
 7               exit(0)
 8     }
 9         else{
10               p->freq++;
11               p->next->pred=p->pred;
12               p->pred->next=p->next;
13               q=p->pred;
14               while(q->freq<p->freq&&q!=L)
15                        q=q->pred;
16               p->next=q->next;
17               q->next->pred=p;
18               q->next=p;
19               p->pred=q;
20     }
21              return p;          //返回值為x的結點的指針
22 }
View Code

 歡迎查看關於順序表的學習,見上篇http://www.cnblogs.com/tracylining/p/3394038.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM