天天看點

全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)

定義一個方法(函數),實作輸入一個連結清單的頭結點,然後可以反轉這個連結清單的方向,并輸出反轉之後的連結清單的頭結點。

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

全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)

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

全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)

這樣的話,無法繼續周遊 i 以後的結點了,那麼自然想到,在斷鍊之前,提前儲存之前的狀态。那麼自然想到定義三個指針,分别指向目前結點 i,i 的後繼 j,i 的前驅 h 結點。儲存斷鍊之前的三個結點的連接配接狀态。然後,假設沒問題了,那麼繼續反轉完畢,最後連結清單的尾結點就是反正連結清單的頭結點了,也就是 next 為 null 的結點,是原始連結清單的尾結點。

全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)
全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)

定義的這個三個指針,目的就是防止斷鍊之後無法繼續周遊連結清單以後的結點,實作全部的反轉。當 pnow 的 next 指向 pnow 的前驅pre(初始化是 null)的時候,已經實作了 pnow 和前驅pre的方向反轉,但是 pnow 此時就和後繼pnext斷鍊了,那麼使用 pre 後移的方式,指向 pnow,同時 pnow 也後移,指向 pnext,而 pnext 繼續指向更新之後的 pnow 的 next 結點即可。進而實作了狀态的儲存,繼續周遊全部結點,實作連結清單反轉。

注意關于連結清單問題的常見注意點的思考:

1、如果輸入的頭結點是 null,或者整個連結清單隻有一個結點的時候

2、連結清單斷裂的考慮

下面看看遞歸的實作方式

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

全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)
全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)
全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)

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

全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)

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

全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)

傳回 newhead,也就是新的反轉之後的連結清單(臨時的),然後進入到遞歸工作棧裡的第一個遞歸函數,如圖:

全面分析再動手的習慣:連結清單的反轉問題(遞歸和非遞歸方式)

傳回 newhead,也就是反轉之後的連結清單,此時遞歸工作棧的函數全部執行,傳回的結點就是反轉之後的連結清單的頭結點(之前的尾結點)

辛苦的勞動,轉載請注明出處,謝謝……

http://www.cnblogs.com/kubixuesheng/p/4394509.html