翻轉鏈表-迭代和遞歸雙版本


將一個鏈表翻轉,如 1->2->3->4 變成 4->3->2->1 的鏈表。這是一個非常著名的面試題,看似非常的簡單,但實際上非常的tricky.

實現方法可以有遞歸和迭代兩種方法,這兩個算法也都保證了in-place 和 one-pass. 所以效率還是很高的。這篇文章主要基於http://leetcode.com/2010/04/reversing-linked-list-iteratively-and.html. 他講解的基本已經比較清楚,我就再從比較菜的角度去分析一下。

 

迭代方法:

首先來看迭代版本的基本思路,先上圖:

上圖就是迭代的第一步,基本就是prev, curr, next三個指針跟蹤着當前點的情況,然后更新后向前移動,直到移到鏈表末尾

看代碼后應該更清晰:

 1 void reverse(Node*& head) {
 2   if (!head) return;
 3   Node* prev = NULL;
 4   Node* curr = head;
 5   while (curr) {
 6     Node* next = curr->next;
 7     curr->next = prev;
 8     prev = curr;
 9     curr = next;
10   }
11   head = prev;
12 }

代碼中要注意 Node*&, 畢竟是改變了鏈表的結構,所以鏈表的頭指針應該傳一個引用。

翻轉鏈表的迭代版本很清晰,且容易實現。而下面的遞歸方法雖然代碼很短,但是比較難理解,且實現起來細節也很多。

 

遞歸版本:

這個整體過程比較復雜,所以還是先上圖:

然后來看着代碼分析:

void reverse(Node*& p) {
  if (!p) return;
  Node* rest = p->next;
  if (!rest) return;
  reverse(rest);
  p->next->next = p;
  p->next = NULL;
  p = rest;
}

遞歸過程中,首先迅速由遞歸函數進行至第一幅圖的情況(其實下面還有一步,此時rest 指向NULL,返回到第一幅的情況)

然后將當前節點的下一節點的子節點倒指向其父節點(即當前節點),然后將當前節點指向NULL,即當前我們翻轉了從當前節點到末尾節點。

注意這之后的最后一個語句,將p 賦值為 rest,然后返回。 返回到上一層會發生什么呢? 這就要注意每層調用reverse函數時,傳進去的指針式rest, 即修改上一層的rest指針為當前層的rest指針,換句話說,在以后遞歸的過程中,rest指針將一直指向最后一個節點,即新的鏈表根節點。


免責聲明!

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



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