1. 程式人生 > >單鏈表題錦集

單鏈表題錦集

1.刪除不帶頭節點的單鏈表L中所有值為x的結點

思路1:遞迴演算法

void Delete_X(Linklist &L,ElemType x){
    LNode *p;
    if(!L) return NULL;
    if(L->data==x){
        p=L;       //刪除 L,並讓 l 指向下一個節點 
        L=L->next;
        delete p;
        Delete(L,x);
    }else
        Delete(L->next,x);
}

評價 :此演算法需要一個工作棧,深度O(n),時間複雜度o(n)。上述程式碼直接delete p不會斷鏈,L為引用,直接對原連結串列操作,不會斷鏈。
思路2:常規遍歷

void Delete_X(Linklist &L,ElemType x){
    LNode *pre=L,*p;   //  p是檢測指標,pre是p的前驅
    while(!p)
        if(p->data==x){ //尋找被刪結點
            pre->next=p->next;
            delete p;      
            p=p->next;
        }else{
        pre=p;
        p=p->next;
    }
}

2.帶頭結點連結串列排序(假設升序)

思路:採用直接插入排序演算法思想,先構造成只含一個數據結點的有序單鏈表,然後依次掃描單鏈表中剩餘結點 *p (直至p==NULL),在在有序表中通過比較查詢 *p 的前驅結點 *pre ,然後將 *p 插入到 *pre之後。

void *Sort(Linklist &L){
    LNode *p=L->next,*pre;
    LNode *r=p->next;//r保持 *p 的後繼指標,以保證不斷鏈 
    p->next=NULL;//構造只含一個數據節點的有序表 
    p=r;
    while(!p){
        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),為提升時間效能,可先將連結串列資料複製到陣列中,然後採用時間複雜度o(nlog2 n)的演算法進行排序,然後依次將資料元素依次插入連結串列,此時的時間複雜度o(nlog2 n),顯然,是以空間換時間

3.反向輸出帶頭結點的單鏈表

思路1:採用棧的思想,用遞迴實現

viod L_print(Linklist &L){
    if(L->next!=NULL) {
        L_print(L->next);
    }
    print(L->data); //輸出函式 
} 

思路2:採用就地逆置,然後遍歷列印(程式碼在逆置處給出)

4.將帶頭節點的單鏈表逆置

思路1:將頭結點摘下,然後從第一個結點開始,依次頭插法建立單鏈表,然後直至最後一個結點,over

Linklist Revrese(Linklist &L){
    LNode *p,r; //p為工作指標,r為p的後繼,以防斷鏈
    L->next=NULL; //摘頭結點 
    p=L->next;  //從第一個資料結點開始
    while(p!=NULL){|
      r=p->next; //
      p->next=L->next; //p插到 l 後面 
      L->next=p;
      p=r; 
    } 
    return L;
} 

評價 :此演算法時間複雜度o(n),空間複雜度o(1)
思路2:不斷遍歷連結串列,將後一個節點的next指向前一個節點
最後頭節點掛到原連結串列的尾部

Linklist Reverse(Linklist &L){
    LNode *p=L,*q,*pr; //pr 指向待轉置結點的下一個結點
    p=L->next ;
    L->next =NULL; //摘除頭節點 
    q=NULL;  
    while(p){
        pr=p->next;
        p->next=q; //後一個節點的next指向前一個節點
        q=p;   //尾指標為空 
        p=pr;  //指標後移 
     } 
     L->next =q; //頭節點掛到原連結串列的尾部
 } 

評價 :複雜度與思路一樣

5.找兩單鏈表的公共結點

思路:分別遍歷兩個連結串列,得到兩個連結串列的長度,並求差。在長的連結串列先遍歷長度之差個結點,然後同步遍歷,直到找到相同結點,或者一直到連結串列結束

Linklist Serach_Common(Linklist L1,Linklist L2){
    int len1=Length(L1),len2=Length(L2);
    Linklist longlist,shortlist;
    if(len1>len2){ 
        longlist=L1->next;
        shortlist=L2->next;
        dist=len1-len2; 
    }else{
        longlist=L2->next;
        shortlist=L1->next;
        dist=len2-len1; 
    }
    while(dist--)
        longlist=longlist->next;// 表長的先遍歷dist步 

    while(longlist!=NULL){ //同步遍歷 
        if(longlist==shortlist)
          return longlist; 
        else{
            longlist=longlist->next;
            shortlist=shortlist->next;
        }
    }//while
    return NULL;
}

評價 :時間複雜度O(len1+len2)

7.輸出單鏈表中倒數第k個結點

思路:雙指標,先讓其中一個指標走k步,然後同步走,一遍掃描即可完成

bool Search(Linklist &L,int k){
    LNode *p=L->next,*q=L->next;
    int count=0;
    while(!p){
        if(cout<k) count++l; //先 p 移動 k 步 ,之後同步移動 p,q 
        else q=q->next;
        p=p->next;
    }
    if(count<k) return false;
    else cout<<q->data<<endl;
    return true; 
}   

6.奇偶連結串列,即奇序號結點放前面,偶序號結點放後面(出自Leetcode)

例如:1,輸出1
1,2,輸出1,2
1,2,3 輸出1,3,2;
1,2,3,4 輸出1,3,2,4 以此類推
思路:奇偶結點交替存在,奇號結點連結的總是偶的下一個結點,而偶號結點連結的總是奇的下一個結點,由此可在遍歷原單鏈表過程完成迭代

Linklist Function(Linklist &L){  //AC code
    LNode *odd=L->next;    //連結奇序號結點 
    LNode *even=odd->next; //連結偶序號結點
    LNode *Even=even;
    while(odd->next && even->next ){ //連結串列結點數超過兩個輸出才發生變化 
        odd->next=even->next;       //奇號結點連結的總是偶的下一個結點 
        odd=even->next ;            // 奇號結點後移兩部 
        even->next=odd->next;       //偶號結點連結的總是奇的下一個結點
        even=odd->next ;            // 偶號結點後移兩部 
    }
    odd->next=Even;     //偶數號結點頭連結到奇數號結點尾部 
    return L;
} 

評價:一邊遍歷即可完成,時間複雜度o(n),空間複雜度o(1)

7.求連結串列的中位數(不帶頭結點)

例如:1,輸出 1
1,2,輸出 1
1,2,3 輸出 2 以此類推
思路:採用雙指標,快慢連結串列,quick走兩步,slow走一步,quick走到頭,slow則走到中位數處

int Getmin( Linklist L){ //AC code
    LNode *quick=L;
    LNode *slow=L;
    if(slow==NULL) return NULL;
    while(quick->next !=NULL){
        if(quick->next->next !=NULL){
            slow=slow->next ;
            quick=quick->next->next ;
        }
        else{
            quick=quick->next ;
        }
    }
    return (slow->data );
}

8.假設A,B連結串列遞增有序(帶頭結點),求A,B的公共結點,要求不破壞A,B的結點

思路:表A,B都有序,可從A,B第一個元素開始依次比較A,B兩表元素,若倆元素不相等,小的對應指標後移,若元素值相等,建立一個值等於倆結點的元素值的新結點,使用尾插法插入到新的連結串列中,並且兩個原表指標後移一位,直到其中一個遍歷到表位

void Get_Common(Linklist A,Linklist B){
    LNode *p=A->next,*q=B->next,*r,*s;
    Linklist C=new LNode ; //建立表 C 
    r=C;
    while(p!=NULL && q!=NULL){
        if(p->next <q->next ) //A 當前元素小,指標後移 
          p=p->next ;
        else if(p->next >q->next) //B 當前元素小,指標後移
          q=q->next ; 
        else{                     // 找到公共結點 
            s=new LNode;
            s->data =p->data ;  //尾插法插入*s結點 
            r->next=s;
            r=s;
            p=p->next ;  //A,B繼續向後掃描 
            q=q->next ;
        }   
    }
    r->next =NULL; // 置 C 尾結點指標為空 
} 

9.假設A,B連結串列遞增有序(帶頭結點),編寫函式,求A,B的交集,放於A連結串列中

思路:採用歸併思想,設定兩個工作指標pa,pb,對連結串列掃描,只有同時存在在倆集合中的元素才連結到結果表中且僅保留一個,其他結點全部釋放。當一個連結串列遍歷完畢,釋放另一個表中的全部結點。

//AC 函式
Linklist Union(Linklist la,Linklist lb){
    LNode *pa=la->next ,*pb=lb->next ;
    LNode *pc=la;            //結果表中當前合併結點的前驅指標;
    LNode *u;              // u指標用於指向待釋放結點空間 
    while(pa && pb){
        if(pa->data ==pb->data ){
            pc->next =pa;
            pc=pa;
            pa=pa->next ;
            u=pb;        
            pb=pb->next ;
            delete pb; 
        }else if(pa->data <pb->data ){
            u=pa;
            pa=pa->next ;
            delete u;
        }else {
            u=pb;
            pb=pb->next;
            delete pb;
        }
    }
    while(pa){  // B 遍歷完, A 未結束 
        u=pa;
        pa=pa->next ;
        delete u;
    }
    while(pb){
        u=pb;
        pb=pb->next ;
        delete pb;
    }
    pc->next =NULL; //置結果表的尾指標為空 
    delete lb; //釋放 B 的頭結點
    return la;
}