全面分析再動手的習慣:鏈表的反轉問題(遞歸和非遞歸方式)


定義一個方法(函數),實現輸入一個鏈表的頭結點,然后可以反轉這個鏈表的方向,並輸出反轉之后的鏈表的頭結點。

typedef struct Node{
    int data;
    Node *next;
} Node, *List;

鏈表類的問題,涉及到了很多指針的操作,需要嚴謹的分析,全面的分析問題之后,在開始寫代碼,磨刀不誤砍柴工!反轉鏈表,直接的想法,就是把鏈表中指針的方向反轉就可以了,如圖所示:

假設 i 結點之前,我們把所有的結點的指針都已經反轉了,那么自然 i 和以后的結點鏈接發生了斷裂!如下圖;

這樣的話,無法繼續遍歷 i 以后的結點了,那么自然想到,在斷鏈之前,提前保存之前的狀態。那么自然想到定義三個指針,分別指向當前結點 i,i 的后繼 j,i 的前驅 h 結點。保存斷鏈之前的三個結點的連接狀態。然后,假設沒問題了,那么繼續反轉完畢,最后鏈表的尾結點就是反正鏈表的頭結點了,也就是 next 為 null 的結點,是原始鏈表的尾結點。

#include <iostream>
using namespace std;

typedef struct Node{
    int data;
    Node *next;
} Node, *List;

Node * reverseList(List head){
    //定義三個指針,保存原來的連接的狀態
    //當前結點指針
    Node *pnow = head;
    //當前結點的前驅指針,初始化是 NULL
    Node *pre = NULL;
    //當前結點的后繼指針,初始化也是 null
    Node *pnext = NULL;
    //定義尾指針
    Node *tail = NULL;
    //開始遍歷鏈表
    while(pnow != NULL){
        //如果當前結點不是 null,那么初始化 pnext 指針指向當前結點的下一個結點
        pnext = pnow->next;
        //如果找到了尾結點,初始化 tail 指針
        if(NULL == pnext){
            tail = pnow;
        }
        //進行鏈表的反轉,當前結點的 next 指針指向前一個結點,實現鏈表方向的反轉,此時發生了斷鏈
        pnow->next = pre;
        //勿忘斷鏈的情形,需要使用 pre 指針保存狀態,pre 等價於是后移一個結點
        pre = pnow;
        //pnow 后移一個結點
        pnow = pnext;
    }
    
    return tail;
}

定義的這個三個指針,目的就是防止斷鏈之后無法繼續遍歷鏈表以后的結點,實現全部的反轉。當 pnow 的 next 指向 pnow 的前驅pre(初始化是 null)的時候,已經實現了 pnow 和前驅pre的方向反轉,但是 pnow 此時就和后繼pnext斷鏈了,那么使用 pre 后移的方式,指向 pnow,同時 pnow 也后移,指向 pnext,而 pnext 繼續指向更新之后的 pnow 的 next 結點即可。從而實現了狀態的保存,繼續遍歷全部結點,實現鏈表反轉。

注意關於鏈表問題的常見注意點的思考:

1、如果輸入的頭結點是 NULL,或者整個鏈表只有一個結點的時候

2、鏈表斷裂的考慮

下面看看遞歸的實現方式

遞歸的方法其實是非常巧的,它利用遞歸走到鏈表的末端,然后再更新每一個node的next 值 ,實現鏈表的反轉。而newhead 的值沒有發生改變,為該鏈表的最后一個結點,所以,反轉后,我們可以得到新鏈表的head。

//遞歸方式
Node * reverseList(List head)
{
    //如果鏈表為空或者鏈表中只有一個元素
    if(head == NULL || head->next == NULL)
    {
        return head;
    }
    else
    {
        //先反轉后面的鏈表,走到鏈表的末端結點
        Node *newhead = reverseList(head->next);
        //再將當前節點設置為后面節點的后續節點
        head->next->next = head;
        head->next = NULL;
        
        return newhead;
    }
}

程序剛開始執行,if 語句失效,進入 else 語句,然后執行Node *newhead = reverseList(head->next);第二個結點的指針參數傳入遞歸函數,一直到,最后一個結點的指針參數傳入遞歸函數,if 語句有效head->next == NULL,返回當前的head 給 newhead 指針指向,如圖:

其實在遞歸函數棧內,按照后進先出的順序,執行一級級的遞歸函數,返回末位結點給 newhead 之后,執行遞歸棧里的第二個遞歸函數,發生如圖

返回 newhead,也就是新的反轉之后的鏈表(臨時的),然后進入到遞歸工作棧里的第一個遞歸函數,如圖:

返回 newhead,也就是反轉之后的鏈表,此時遞歸工作棧的函數全部執行,返回的結點就是反轉之后的鏈表的頭結點(之前的尾結點)

 

歡迎關注

dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

 


免責聲明!

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



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