二叉樹的遍歷方式(遞迴、非遞迴)——Java實現
二叉樹作為一種常用的資料結構,也是面試經常被問到的知識點,瞭解二叉樹的結構和性質也是很有必要的,對於眾多的樹結構,二叉樹只是入門的一種,先把二叉樹理解通透,再深入學習時,會更簡單一些。
二叉樹的性質:
(1) 在非空二叉樹中,第i層的結點總數不超過 , i>=1;
(2) 深度為h的二叉樹最多有
個結點(h>=1),最少有h個結點;
(3) 對於任意一棵二叉樹,如果其葉結點數為N0,而度數為2的結點總數為N2,則N0=N2+1;
(4) 具有n個結點的完全二叉樹的深度為 (注:[ ]表示向下取整)
(5)有N個結點的完全二叉樹各結點如果用順序方式儲存,則結點之間有如下關係:
若I為結點編號則 如果I>1,則其父結點的編號為I/2;
如果2*I<=N,則其左孩子(即左子樹的根結點)的編號為2*I;若2*I>N,則無左孩子;
如果2*I+1<=N,則其右孩子的結點編號為2*I+1;若2*I+1>N,則無右孩子。
(6)給定N個節點,能構成h(N)種不同的二叉樹。
h(N)為卡特蘭數的第N項。h(n)=C(2*n,n)/(n+1)。
(7)設有i個枝點,I為所有枝點的道路長度總和,J為葉的道路長度總和J=I+2i
相關術語:
樹的結點(node):包含一個數據元素及若干指向子樹的分支;
孩子結點(child node):結點的子樹的根稱為該結點的孩子;
雙親結點:B 結點是A 結點的孩子,則A結點是B 結點的雙親;
兄弟結點:同一雙親的孩子結點; 堂兄結點:同一層上結點;
祖先結點: 從根到該結點的所經分支上的所有結點子孫結點:以某結點為根的子樹中任一結點都稱為該結點的子孫結點層:根結點的層定義為1;根的孩子為第二層結點,依此類推;
樹的深度:樹中最大的結點層
結點的度:結點子樹的個數
樹的度: 樹中最大的結點度。
葉子結點:也叫終端結點,是度為 0 的結點;
分枝結點:度不為0的結點;
有序樹:子樹有序的樹,如:家族樹;
無序樹:不考慮子樹的順序;
看完二叉樹的性質和相關屬於,再來看一下二叉樹的遍歷。二叉樹有很多種遍歷方法,最基本的有三種:
1.前序遍歷:對於下圖二叉樹的前序遍歷結果為:1 2 4 8 9 5 10 3 6 7
2.中序遍歷:對於下圖二叉樹的中序遍歷結果為:8 4 9 2 10 5 1 6 3 7
3.後序遍歷:對於下圖二叉樹的後 序遍歷結果為:8 9 4 10 5 2 6 7 3 1
一、前序遍歷
基本思想:先訪問根結點,再先序遍歷左子樹,最後再先序遍歷右子樹。
程式碼實現:
遞迴方法
/**
public class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
*/
private void preOrderBinTree(TreeNode root) {
if (root == null) return;
System.out.print(root.val + " "); //輸出結果 1 2 4 8 9 5 10 3 6 7
if (root.left != null)
preOrderBinTree(root.left);
if (root.right != null)
preOrderBinTree(root.right);
}
非遞迴,用棧實現
private void preIterateBinTree(TreeNode root) {
if(root == null) return;
Stack<TreeNode> s = new Stack<>();
TreeNode node = root;
while (node != null || s.size() > 0) {
if (node != null) {
System.out.print(node.val + " ");
s.push(node);
node = node.left;
} else {
node = s.pop();
node = node.right;
}
}
}
二、中序遍歷
基本思想:先中序遍歷左子樹,然後再訪問根結點,最後再中序遍歷右子樹即 左—根—右。
遞迴方法
private void inOrderBinTree(TreeNode root) {
if (root == null) return;
if (root.left != null)
inOrderBinTree(root.left);
System.out.print(root.val + " "); //輸出 8 4 9 2 10 5 1 6 3 7
if (root.right != null)
inOrderBinTree(root.right);
}
非遞迴,用棧實現
private void inIterateBinTree(TreeNode root) {
if(root == null) return;
Stack<TreeNode> s = new Stack<>();
TreeNode node = root;
while (node != null || s.size() > 0) {
if (node != null) {
s.push(node);
node = node.left;
} else {
node = s.pop();
System.out.print(node.val + " ");
node = node.right;
}
}
}
三、後序遍歷
基本思想:先後序遍歷左子樹,然後再後序遍歷右子樹,最後再訪問根結點即 左—右—根。
遞迴實現
private void afterOrderBinTree(TreeNode root) {
if (root == null) return;
if (root.left != null)
afterOrderBinTree(root.left);
if (root.right != null)
afterOrderBinTree(root.right);
System.out.print(root.val + " "); //輸出 8 9 4 10 5 2 6 7 3 1
}
非遞迴,棧實現
private void afterIterateBinTree(TreeNode root) {
Stack<TreeNode> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
int i = 1;
while(root != null || !stack1.empty()) {
while (root != null) {
stack1.push(root);
stack2.push(0);
root = root.left;
}
while(!stack1.empty() && stack2.peek() == i)
{
stack2.pop();
System.out.print(stack1.pop().val + " ");
}
if(!stack1.empty())
{
stack2.pop();
stack2.push(1);
root = stack1.peek();
root = root.right;
}
}
}
四、層次遍歷
非遞,佇列歸實現
private void layerIterateBinTree(TreeNode root) {
if(root == null) return;
Queue<TreeNode> q = new ArrayDeque<>();
TreeNode node = root;
while (node != null || !q.isEmpty()) {
if (node != null) {
System.out.print(node.val + " "); //輸出 1 2 3 4 5 6 7 8 9 10
if(node.left != null)
q.add(node.left);
if(node.right != null)
q.add(node.right);
node = null;
} else {
node = q.remove();
}
}
}