p32 面試題2:輸入一個鏈表的頭結點,從尾到頭反過來打印出每一個結點的值。面試
從頭遍歷節點,將全部遍歷到的值都壓入棧,遍歷完以後再從棧彈出值並打印,就實現了從尾到頭打印的要求。bash
public static void reversePrintNodes(Node head) {
Stack<Integer> stack = new Stack<>();
while (head != null) {
stack.push(head.data);
head = head.next;
}
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
複製代碼
遞歸的本質是棧。數據結構
這句話應該如何理解?函數
須要從起源去理解:計算機發展過程當中,爲了表示函數調用自身的場景,不得不建立一種叫 Stack 的數據結構,來表達這種函數調用形式,並將其稱爲 recursion,即遞歸。post
詳見下文:《虛擬機棧和棧幀的起源和關係》ui
理解遞歸的前提是先理解 Stack:入棧就是先從頭一個個元素積壓到最後一個,取元素(即出棧)則只能從最後一個挨個取到最底層 -- 先進後出,先來的壓到最底,最後才彈出。spa
擴展到遞歸:3d
入棧階段:
Basic Notation:棧幀 Stack Frame【切成一片片的小棧叫棧幀】
函數第一次被調用,壓入方法棧底的棧幀(未執行到返回語句,棧幀還得活着)
函數第二次被調用,壓到方法棧第二層棧幀(未執行到返回語句,棧幀還得活着)
函數第三次被調用,壓到方法棧第三層棧幀(未執行到返回語句,棧幀還得活着)
······
函數最後一次被調用,壓到方法棧最頂層(執行到返回語句,棧幀能夠撤走了)
出棧階段:
最頂層的方法執行到返回語句,將 Base Case (觸底了) 的執行結果返回給上一個棧幀,而後撤走當前棧幀(即彈出棧)。
此時返回值做爲參數傳入到倒數第二個棧幀,被其中的函數做爲計算的參數,執行到返回語句時,
同上,將結果返回給上一個棧幀,而後撤走當前棧幀。
······
出棧到最底下的那個棧幀,前面全部的計算結果一個傳遞給上一個,累積計算,直到傳到祖宗十八代這裏,宣告大結局。
這一代拿到前面十八代祖宗流傳下來的值,計算完成,經過執行返回語句,將最終結果交給大boss,
出棧,這個家族的使命就完美結束了。
以上,即遞歸的整個流程分析。
複製代碼
遞歸寫法以下:code
public static void reversePrintNodesRecursively(Node head) {
if (head == null) {
return;
}
Node tmpNode = head.next;
if (tmpNode != null) {
reversePrintNodesRecursively(tmpNode);
}
System.out.println(head.data);
}
複製代碼