前言
本系列的文章爲筆者學習《劍指offer第二版》後的筆記整理以及Java實現,解法不保證與書上一致。
另外,我最近在系統整理一些 Java 後臺方面的面試題和參考答案,有找工作需求的童鞋,歡迎關注我的 Github 倉庫,如果覺得不錯可以點個 star 關注 :
題目描述
輸入一個鏈表的頭節點,從尾到頭反過來打印出每個節點的值。鏈表節點定義如下:
private class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
解題思路
思路一:使用棧
由於需要從頭到尾遍歷鏈表,但打印鏈表順序則相反,要從尾到頭打印。也就是說第一個遍歷的節點要最後一個輸出,而最後一個遍歷的節點第一個輸出。這是很典型的後進先出
,自然而然可以選擇棧
來實現。接下來我們可以這樣做:
- 從頭到尾遍歷鏈表,每經過一個節點時,將其放入到一個棧中;
- 遍歷完整個鏈表之後,棧中現在的順序是頭節點位於棧底,尾節點位於棧頂;
- 從棧頂開始逐個輸出節點的值,此時輸出的節點的順序即相當於從尾到頭反過來打印鏈表中每個節點的值。
package com.offers.chapter2.question06;
import java.util.ArrayList;
import java.util.LinkedList;
/**
* 輸入一個鏈表的頭節點,從尾到頭反過來打印出每個節點的值。
*
* 使用棧的方法
*
* @author Rotor
* @since 2019/9/30 10:22
*/
public class PrintListFromTail2Head {
// 定義鏈表節點類
private class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
/**
* 使用棧,從頭到尾掃描並壓入節點,最終尾節點位於棧頂
*
* @param listNode 鏈表的頭結點
* @return 從頭到尾排列的鏈表節點
*/
public ArrayList<Integer> printListReversingly_Iteratively(ListNode listNode) {
LinkedList<Integer> stack = new LinkedList<>();
// 定義一個臨時節點,指向鏈表頭
ListNode node;
for (node = listNode; node != null; node = node.next) {
stack.push(node.val);
}
// 返回從尾到頭排列的鏈表節點
return new ArrayList<>(stack);
}
}
思路二:使用遞歸
既然想到了用棧來實現,而遞歸本質上就是一個棧結構,自然而然地就又想到了用遞歸的方式來實現。現在要反過來輸出鏈表,我們可以這麼做:
- 每訪問一個節點的時候,先遞歸輸出它後面的節點;
- 然後再輸出該節點自身,這樣鏈表的輸出結果就反過來了。
package com.offers.chapter2.question06;
import java.util.ArrayList;
/**
* 輸入一個鏈表的頭節點,從尾到頭反過來打印出每個節點的值。
*
* 使用棧的方法
*
* @author Rotor
* @since 2019/9/30 10:22
*/
public class PrintListFromTail2Head {
// 類成員變量,用於封裝遞歸時從後往前輸出的鏈表節點
private ArrayList<Integer> array = new ArrayList<>();
// 定義鏈表節點類
private class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
/**
* 使用遞歸,先遞歸到最後一個結點後開始依次返回。
* 但如果鏈表如果很長則不適合用遞歸,因爲遞歸深度將很大。
*
* @param listNode 表的頭結點
* @return 從頭到尾排列的鏈表節點
*/
public ArrayList<Integer> printListReversingly_Recursively(ListNode listNode) {
if (listNode != null) {
if (listNode.next != null) {
printListReversingly_Recursively(listNode.next);
}
array.add(listNode.val);
}
return array;
}
}
上面遞歸看着很簡潔,但如果鏈表過長則不適用,因爲鏈表過長會導致遞歸深度很大,從而有可能導致方法調用棧溢出。
總結
- 從尾到頭打印輸出每個鏈表節點的值時,雖然從頭到尾輸出很簡單,可以把鏈表中鏈接節點的指針反轉過來,改變鏈表的方向,然後就可以從頭到尾輸出了。但是這麼做會改變鏈表的結構。
- 從爲到頭打印鏈表節點的值可以使用棧或者遞歸的方法來解決,但是鏈表很長時可能會導致遞歸深度很大,從而導致方法調用棧溢出,因此使用哪種方式需要好好權衡。
後記
如果你同我一樣想要努力學好數據結構與算法、想要刷 LeetCode 和劍指 offer,歡迎關注我 GitHub 上的 LeetCode 題解:awesome-java-notes