單鏈表題錦集
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;
}