劍指Offer題目5:從未到頭打印鏈表(含遞歸分析)(Java)

p32 面試題2:輸入一個鏈表的頭結點,從尾到頭反過來打印出每一個結點的值。面試

思路分析

  1. 藉助Stack數據結構實現

從頭遍歷節點,將全部遍歷到的值都壓入棧,遍歷完以後再從棧彈出值並打印,就實現了從尾到頭打印的要求。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());
    }
}
複製代碼

  1. 遞歸實現

遞歸的本質是棧。數據結構

這句話應該如何理解?函數

須要從起源去理解:計算機發展過程當中,爲了表示函數調用自身的場景,不得不建立一種叫 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);
}
複製代碼

相關文章
相關標籤/搜索