劍指Offer面試題5(Java版):從尾到頭列印連結串列

完美風暴4發表於2015-07-25

題目:輸入一個連結串列的頭結點,從尾到頭反過來列印出每個節點的值。


看到這道題,很多人的第一反應是從頭到尾輸出將會比較簡單,於是我們很自然的想到把連結串列中的節點的指標反轉過來,改變連結串列的方向,然後就可以從頭到尾輸出了。但該方法改變原來連結串列的結構。是否允許在列印連結串列的時候修改連結串列的結構?這個取決於面試官的要求,因此在面試的時候我們要詢問清楚面試官的要求。

通常列印是一個只讀操作,我們不希望列印時修改內容。假設面試官也要求這個題目不能改變連結串列的結構。

接下來我們想到解決這個問題肯定要遍歷連結串列。遍歷的順序是從頭到尾的順序,可輸出的順序卻是從尾到頭。也就是說第一個遍歷的節點最後一個輸出,而最後一個遍歷到的節點第一個輸出。這就是典型的'後進先出“,我們可以從棧實現這種順序。沒經過一個節點的時候,把該節點放到一個棧中。當遍歷完整的連結串列後,再從棧頂開始逐個輸出節點的值,此時輸出的節點的順序已經反轉過來了。

既然想到了用棧來實現這個函式,而遞迴在本身上就是一個棧結構,於是自然就想到了用遞迴來實現。要實現反過來輸出連結串列,我們沒訪問到一個節點的時候,先遞迴輸出後面的 節點,再輸出該節點本身,這樣連結串列的輸出結果就反過來了。


實現程式碼如下:

<pre name="code" class="java">package offer;

import java.util.Stack;

class Node {
	public int data;
	public Node nextNode;

	public Node() {

	}

	public Node(int data) {
		this.data = data;
	}
}

public class E05FromTailToStart {

	public static void main(String[] args) {
		Node start = new Node(1);
		Node two = new Node(2);
		start.nextNode = two;
		
		// Node two = new Node(2);
		// start.nextNode = two;
		printTailToStartRec(start);
		printTailToStartStack(start);

	}

	// 採用遞迴呼叫的方法
	private static void printTailToStartRec(Node start) {
		if (start != null) {
			if (start.nextNode != null) {
				printTailToStartRec(start.nextNode);
			}
			System.out.println(start.data);
		}
		else{
			System.out.println("list is null!");
		}
	}

	// 採用棧--先進後出的方法
	private static void printTailToStartStack(Node node) {
		if (node == null) {
			System.out.println("list is null");
			return;
		}

		Stack<Node> stack = new Stack<Node>();
		while (node != null) {
			stack.push(node);
			node = node.nextNode;
		}
		while (!stack.isEmpty()) {
			System.out.println(stack.pop().data);
		}

	}
}


上面的基於遞迴的程式碼看起來很簡潔,但有個問題:當連結串列非常長的時候,就會導致函式呼叫的層級很深,從而有可能導致函式呼叫棧溢位。顯式用棧基於迴圈的程式碼的魯棒性要好一些。

相關文章