目錄
二叉樹的基本結構
class Node{
int data;
Node left;
Node right;
Node(int data){
this.data = data;
}
}
每個node擁有左和右兩個子節點幷包含自己的數據值。
什麼是序
每個節點遍歷過程中都會被訪問三次,分別是: 遍歷到當前節點(第一次)-> 該節點左子樹 -> 返回當前節點(第二次) -> 該節點右子樹 -> 返回當前節點(第三次).
如果遍歷過程中指針第一次指向節點就打印出該節點的值,此爲先序遍歷。類似的,如果指針第二次指向該節點時打印出節點的值爲中序遍歷,第三次訪問時打印出值爲後序遍歷。
二叉樹遍歷的遞歸版本
遞歸先序(中 -> 左 -> 右)
在遞歸過程中,將打印語句放在函數的開始部分,爲先序遍歷,先序遍歷在第一次訪問該節點時也就是訪問其左子樹前,打印當前節點的值.
public static void preOrderPrint(Node head){
if (head == null){
return;
}
System.out.println(head.data);
preOrderPrint(head.left);
preOrderPrint(head.right);
}
遞歸中序(左 -> 中 -> 右)
將打印語句放在遍歷左子樹和遍歷右子樹中間爲中序遍歷.訪問到該節點時未打印節點值,在訪問完左子樹之後返回到當前節點,也就是第二次訪問該節點時,打印節點值.
public static void inOrderPrint(Node head){
if (head == null){
return;
}
inOrderPrint(head.left);
System.out.println(head.data);
inOrderPrint(head.right);
}
遞歸後序(右 -> 左 ->中)
依此類推,打印節點放在遍歷左子樹和右子樹的後邊爲後續遍歷.順序爲訪問完左子樹和又子樹之後.
public static void posOrderPrint(Node head){
if (head == null){
return;
}
posOrderPrint(head.left);
posOrderPrint(head.right);
System.out.println(head.data);
}
二叉樹遍歷的非遞歸版本
非遞歸先序
1-拿到一下需要遍歷的根節點之後,建立一個棧,將根節點壓棧.
2-判斷棧中是否爲空,不爲空則彈出站頂內容並打印節點值
3-剛剛彈棧的節點按照先右後左的順序將子節點壓棧.
4-while(!stack.isEmpty)重複2/3步驟.
public static void preOrderUnRecur(Node head){
if (head == null)
return;
Stack<Node> stack = new Stack<>();
stack.push(head);
while (!stack.isEmpty()){
Node node = stack.pop();
System.out.println(node.data);
if (node.right != null)
stack.push(node.right);
if (node.left != null)
stack.push(node.left);
}
}
遞歸函數是一個函數棧,非遞歸的方式只是自己創建了一個棧裏邊壓如我們想要的東西.
爲什麼用棧結構:
二叉樹的結構是從根節點到子節點可尋址,但子節點到根節點不可尋址.我們需要一個結構能讓我們反向回到父節點同時又可以保證遵循從上到下訪問過來的順序,棧結構天然合適.
非遞歸中序(精華)
二叉樹是由n條左邊界構成的。如下圖中,
三個節點的二叉樹可以理解爲由左邊界1 -> 2和節點3組成的。因爲3沒有子節點,所以自成左邊界。也就是說這棵二叉樹由兩條左邊界構成。
如果將樹分爲多個左邊界後,從以root節點爲頭的左邊界開始倒序遍歷,遍歷到每個節點都執行兩個動作
1-打印當前節點的值
2-檢查該節點是否有右節點,如果有,就把以這個右節點作爲頭結點的左邊界壓棧倒序打印
迭代執行1-2兩步即可。
具體執行步驟如下
0-創建一個棧
1-持有頭節點後,只要當前節點不爲null,就壓當前節點的左節點進棧。
2-當前節點爲null,證明整個左邊界全部壓棧了。將節點依次彈出打印。
3-打印時,需要檢查當前節點的右節點是不是null,如果不爲null則該右節點是另一條左邊界的頭結點,迭代執行1、2、3步。
public static void inOrderUnRecur(Node head){
System.out.println("inOrderUnRecur");
Node cur = head;
if (cur == null)
return;
Stack<Node> stack = new Stack<>();
//如果棧裏還有節點就不可以停止
//如果棧裏沒有節點了但是當前節點還有可能存在右節點(不爲null)就不可以停止
while (!stack.isEmpty() || cur != null){
//從頭到最左全部壓棧
if (cur != null){
stack.push(cur);
cur = cur.left;
//如果壓完了,彈出最後一個,檢查是否有右節點,右就繼續壓.
}else {
cur = stack.pop();
System.out.print(head.data + " ");
cur = cur.right;
}
}
}
非遞歸後序
後序遍歷的輸出順序是左 > 右 > 中.先序遍歷的順序是中 > 左 > 右,我們可以先根據先序遍歷的過程實現按照 中 > 右 > 左的順序將節點全部壓入另一個棧,然後遍歷彈出新棧的全部數據達到左 > 右 > 中的順序要求.
public static void posOrderUnRecur(Node head){
if (head == null)
return;
Stack<Node> stack1 = new Stack<>();
Stack<Node> stack2 = new Stack<>();
stack1.push(head);
while (!stack1.isEmpty()){
head = stack1.pop();
stack2.push(head);
if (head.left != null)
stack1.push(head.left);
if (head.right != null)
stack1.push(head.right);
}
while (!stack2.isEmpty()){
System.out.print(stack2.pop().data + " ");
}
}