圖解單向連結串列反轉
如何把一個單鏈表進行反轉?
方法1:將單鏈表儲存為陣列,然後按照陣列的索引逆序進行反轉。
方法2:使用3個指標遍歷單鏈表,逐個連結點進行反轉。
方法3:從第2個節點到第N個節點,依次逐節點插入到第1個節點(head節點)之後,最後將第一個節點挪到新表的表尾。
方法4: 遞迴(相信我們都熟悉的一點是,對於樹的大部分問題,基本可以考慮用遞迴來解決。但是我們不太熟悉的一點是,對於單鏈表的一些問題,也可以使用遞迴。可以認為單鏈表是一顆永遠只有左(右)子樹的樹,因此可以考慮用遞迴來解決。或者說,因為單鏈表本身的結構也有自相似的特點,所以可以考慮用遞迴來解決)
方法1:
浪費空間。
方法2:
使用p和q兩個指標配合工作,使得兩個節點間的指向反向,同時用r記錄剩下的連結串列。
p = head;
q = head->next;
head->next = NULL;
現在進入迴圈體,這是第一次迴圈。
r = q->next;
q->next = p;
p = q;
q =r;
第二次迴圈。
r = q->next
q->next = p;
p = q;
q = r
第三次迴圈。。。。。
具體程式碼如下
ActList* ReverseList2(ActList* head) { //ActList* temp=new ActList; if(NULL==head|| NULL==head->next) return head; //少於兩個節點沒有反轉的必要。 ActList* p; ActList* q; ActList* r; p = head; q = head->next; head->next = NULL; //舊的頭指標是新的尾指標,next需要指向NULL while(q){ r = q->next; //先保留下一個step要處理的指標 q->next = p; //然後p q交替工作進行反向 p = q; q = r; } head=p; // 最後q必然指向NULL,所以返回了p作為新的頭指標 return head; }
updated 2014-01-24,重新非IDE環境寫了一遍 如果覺得上面的先成環再斷環的過程不太好理解,那麼可以考慮下面這個辦法,增加一箇中間變數,使用三個變數來實現。
struct ListNode{ int val; ListNode* next; ListNode(int a):val(a),next(NULL){} }; ListNode* reverseLinkedList3(ListNode* head){ if(head==NULL||head->next==NULL) return head; ListNode* p=head; //指向head ListNode* r=head->next; //指向待搬運的節點,即依次指向從第2個節點到最後一個節點的所有節點 ListNode* m=NULL; //充當搬運工作用的節點 ListNode* tail=head->next; while(r!=NULL){ //bug2 迴圈語句寫錯了, while寫成了if m=r; r=r->next; m->next=p->next; p->next=m; //if(r!=NULL) //std::cout<<"m="<<m->val<<" ,p="<<p->val<<" ,r="<<r->val<<std::endl; //else //std::cout<<"m="<<m->val<<" ,p="<<p->val<<" ,r=NULL"<<std::endl; } head=p->next; tail->next=p; p->next=NULL; tail=p; return head; // bug1 忘記了return }
方法3
還是先看圖,
從圖上觀察,方法是:對於一條連結串列,從第2個節點到第N個節點,依次逐節點插入到第1個節點(head節點)之後,(N-1)次這樣的操作結束之後將第1個節點挪到新表的表尾即可。
程式碼如下:
ActList* ReverseList3(ActList* head) { ActList* p; ActList* q; p=head->next; while(p->next!=NULL){ q=p->next; p->next=q->next; q->next=head->next; head->next=q; } p->next=head;//相當於成環 head=p->next->next;//新head變為原head的next p->next->next=NULL;//斷掉環 return head; }
附:
完整的連結串列建立,顯示,反轉程式碼:
//建立:用q指向當前連結串列的最後一個節點;用p指向即將插入的新節點。 //反向:用p和q反轉工作,r記錄連結串列中剩下的還未反轉的部分。 #include "stdafx.h" #include <iostream> using namespace std; struct ActList { char ActName[20]; char Director[20]; int Mtime; ActList *next; }; ActList* head; ActList* Create() {//start of CREATE() ActList* p=NULL; ActList* q=NULL; head=NULL; int Time; cout<<"Please input the length of the movie."<<endl; cin>>Time; while(Time!=0){ p=new ActList; //類似表達: TreeNode* node = new TreeNode;//Noice that [new] should be written out. p->Mtime=Time; cout<<"Please input the name of the movie."<<endl; cin>>p->ActName; cout<<"Please input the Director of the movie."<<endl; cin>>p->Director; if(head==NULL) { head=p; } else { q->next=p; } q=p; cout<<"Please input the length of the movie."<<endl; cin>>Time; } if(head!=NULL) q->next=NULL; return head; }//end of CREATE() void DisplayList(ActList* head) {//start of display cout<<"show the list of programs."<<endl; while(head!=NULL) { cout<<head->Mtime<<"\t"<<head->ActName<<"\t"<<head->Director<<"\t"<<endl; head=head->next; } }//end of display ActList* ReverseList2(ActList* head) { //ActList* temp=new ActList; if(NULL==head|| NULL==head->next) return head; ActList* p; ActList* q; ActList* r; p = head; q = head->next; head->next = NULL; while(q){ r = q->next; // q->next = p; p = q; // q = r; // } head=p; return head; } ActList* ReverseList3(ActList* head) { ActList* p; ActList* q; p=head->next; while(p->next!=NULL){ q=p->next; p->next=q->next; q->next=head->next; head->next=q; } p->next=head;//相當於成環 head=p->next->next;//新head變為原head的next p->next->next=NULL;//斷掉環 return head; } int main(int argc, char* argv[]) { // DisplayList(Create()); // DisplayList(ReverseList2(Create())); DisplayList(ReverseList3(Create())); return 0; }
方法4: 遞迴
updated: 2014-01-24
因為發現大部分問題都可以從遞迴角度想想,所以這道題目也從遞迴角度想了想。
現在需要把A->B->C->D進行反轉, 可以先假設B->C->D已經反轉好,已經成為了D->C->B,那麼接下來要做的事情就是將D->C->B看成一個整體,讓這個整體的next指向A,所以問題轉化了反轉B->C->D。那麼, 可以先假設C->D已經反轉好,已經成為了D->C,那麼接下來要做的事情就是將D->C看成一個整體,讓這個整體的next指向B,所以問題轉化了反轉C->D。那麼, 可以先假設D(其實是D->NULL)已經反轉好,已經成為了D(其實是head->D),那麼接下來要做的事情就是將D(其實是head->D)看成一個整體,讓這個整體的next指向C,所以問題轉化了反轉D。 上面這個過程就是遞迴的過程,這其中最麻煩的問題是,如果保留新連結串列的head指標呢?想到了兩個辦法。
// 遞迴版的第一種實現,藉助類的成員變數m_phead來表示新連結串列的頭指標。 struct ListNode{ int val; ListNode* next; ListNode(int a):val(a),next(NULL){} }; class Solution{ ListNode* reverseLinkedList4(ListNode* head){ //輸入: 舊連結串列的頭指標 if(head==NULL) return NULL; if(head->next==NULL){ m_phead=head; return head; } ListNode* new_tail=reverseLinkedList4(head->next); new_tail->next=head; head->next=NULL; return head; //輸出: 新連結串列的尾指標 } ListNode* m_phead=NULL;//member variable defined for reverseLinkedList4(ListNode* head) };
第二個辦法是,增加一個引用型引數 new_head,它用來儲存新連結串列的頭指標。 struct ListNode{ int val; ListNode* next; ListNode(int a):val(a),next(NULL){} }; class Solution{ ListNode* reverseLinkedList5(ListNode* head, ListNode* & new_head){ //輸入引數head為舊連結串列的頭指標。new_head為新連結串列的頭指標。 if(head==NULL) return NULL; if(head->next==NULL){ new_head=head; //當處理到了舊連結串列的尾指標,也就是新連結串列的頭指標時,對new_head進行賦值。因為是引用型引數,所以在接下來呼叫中new_head的值逐層傳遞下去。 return head; } ListNode* new_tail=reverseLinkedList5(head->next,new_head); new_tail->next=head; head->next=NULL; return head; //輸出引數head為新連結串列的尾指標。 } };
--------------------- 作者:feliciafay 來源:CSDN 原文:https://blog.csdn.net/feliciafay/article/details/6841115?utm_source=copy 版權宣告:本文為博主原創文章,轉載請附上博文連結!