二叉樹系列(1)——基本概念和遍歷
阿新 • • 發佈:2018-12-21
二叉樹是一種非常重要的資料結構,其在查詢、排序等領域有著非常重要的應用。本文簡單介紹一些關於二叉樹的基本概念,並給出幾種二叉樹的基本遍歷方法。全文程式碼為Java語言實現。
二叉樹的基本概念
1. 二叉樹的定義
定義: 二叉樹是n(n >= 0)個節點所構成的集合,n=0為空樹,n>0是非空樹。
- 有且僅有一個根節點
- 二叉樹的度為小於等於2,即一個父節點最多隻會有兩個子節點
- 二叉樹天生具有遞迴的特性
- 二叉樹的子樹有左右之分,其次序不能顛倒
2. 二叉樹的性質
- 二叉樹的第i層上最多有 2^(i-1) 個節點
- 深度為k的二叉樹最多有 2^k - 1 個節點
二叉樹的兩種特殊形態:
- 滿二叉樹
特點: 深度為k且含有2^k - 1 個節點的樹
- 完全二叉樹
特性:具有n個節點的完全二叉樹的深度為 |log2N| + 1
二叉樹的構造與遍歷
1. 二叉樹的構造
在Java中,一個節點可以用一個類來描述,其三個成員變數分別為:資料域(value)、指向左子樹節點的引用(left)、指向右子樹節點的引用(right)
構造節點的程式碼實現:
/**
* 二叉樹節點
*/
private static class TreeNode {
// 資料域
private String value;
// 指向左子樹的引用
private TreeNode left;
// 指向右子樹的引用
private TreeNode right;
public TreeNode(String value, TreeNode left, TreeNode right) {
this.value = value;
this.left = left;
this.right = right;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
}
2. 二叉樹的遍歷
二叉樹遍歷的三種方式如下:
(1) 先序遍歷:根節點->左子樹->右子樹
下面是兩種實現方式:遞迴實現和非遞迴實現
/**
* 遞迴實現 先序遍歷:根節點->左子樹->右子樹
* 1. 訪問根節點
* 2. 先序遍歷左子樹
* 3. 先序遍歷右子樹
*/
private void preOrderR(TreeNode node) {
if (node != null) {
// 1. 訪問根節點
System.out.print(node.getValue() + " ");
// 2. 先序遍歷左子樹
preOrderR(node.getLeft());
// 3. 先序遍歷右子樹
preOrderR(node.getRight());
}
}
/**
* 非遞迴實現 先序遍歷
*/
private void preOrder() {
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
TreeNode current = null;
while (!stack.isEmpty()) {
current = stack.pop();
System.out.print(current.getValue() + " ");
if (current.getRight() != null) {
stack.push(current.getRight());
}
if (current.getLeft() != null) {
stack.push(current.getLeft());
}
}
System.out.println();
}
(2) 中序遍歷:左子樹->根節點->右子樹
下面是兩種實現方式:遞迴實現和非遞迴實現
/**
* 遞迴實現 中序遍歷:左子樹->根節點->右子樹
* 1. 中序遍歷左子樹
* 2. 訪問根節點
* 3. 中序遍歷右子樹
*/
private void inOrderR(TreeNode node) {
if (node != null) {
// 1. 中序遍歷左子樹
inOrderR(node.getLeft());
// 2. 訪問根節點
System.out.print(node.getValue() + " ");
// 3. 中序遍歷右子樹
inOrderR(node.getRight());
}
}
/**
* 非遞迴實現 中序遍歷
*/
private void inOrder() {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode current = root;
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.getLeft();
}
if (!stack.isEmpty()) {
current = stack.pop();
System.out.print(current.getValue() + " ");
current = current.getRight();
}
}
System.out.println();
}
(3) 後續遍歷:左子樹->右子樹->根節點
下面是兩種實現方式:遞迴實現和非遞迴實現
/**
* 遞迴實現 後序遍歷:左子樹->右子樹->根節點
* 1. 後續遍歷左子樹
* 2. 後續遍歷右子樹
* 3. 訪問根節點
*/
private void lastOrderR(TreeNode node) {
if (node != null) {
// 1. 後序遍歷左子樹
lastOrderR(node.getLeft());
// 2. 後序遍歷右子樹
lastOrderR(node.getRight());
// 3. 訪問根節點
System.out.print(node.getValue() + " ");
}
}
/**
* 非遞迴實現 後續遍歷
*/
private void lastOrder() {
TreeNode rNode = null;
TreeNode current = root;
Stack<TreeNode> stack = new Stack<TreeNode>();
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.getLeft();
}
current = stack.pop();
while (current != null && (current.getRight()) == null || current.getRight() == rNode) {
System.out.print(current.getValue() + " ");
rNode = current;
if (stack.isEmpty()) {
System.out.println();
return;
}
current = stack.pop();
}
stack.push(current);
current = current.getRight();
}
}
(4) 驗證結果
下面是任意給出的一棵二叉樹:
驗證上面的三種遍歷方式:
public class ConstructBinaryTree {
// 根節點
private TreeNode root;
public ConstructBinaryTree(TreeNode root) {
this.root = root;
}
public TreeNode getRoot() {
return root;
}
public void setRoot(TreeNode root) {
this.root = root;
}
public static void main(String[] args) {
// 第三層節點
TreeNode l3_1 = new TreeNode("F", null, null);
TreeNode l3_2 = new TreeNode("G", null, null);
TreeNode r3 = new TreeNode("H", null, null);
// 第二層節點
TreeNode l2 = new TreeNode("E", l3_2, r3);
TreeNode r2 = new TreeNode("D", l3_1, null);
// 第一層節點
TreeNode l1 = new TreeNode("B", null, r2);
TreeNode r1 = new TreeNode("C", l2, null);
TreeNode root = new TreeNode("A", l1, r1);
ConstructBinaryTree cbt = new ConstructBinaryTree(root);
System.out.println("遞迴 先序遍歷:");
cbt.preOrderR(cbt.getRoot());
System.out.println("\n非遞迴 先序遍歷:");
cbt.preOrder();
System.out.println("\n遞迴 中序遍歷:");
cbt.inOrderR(cbt.getRoot());
System.out.println("\n非遞迴 中序遍歷:");
cbt.inOrder();
System.out.println("\n遞迴 後序遍歷:");
cbt.lastOrderR(cbt.getRoot());
System.out.println("\n非遞迴 後序遍歷:");
cbt.lastOrder();
}
}
結果如下:
遞迴 先序遍歷:
A B D F C E G H
非遞迴 先序遍歷:
A B D F C E G H
遞迴 中序遍歷:
B F D A G E H C
非遞迴 中序遍歷:
B F D A G E H C
遞迴 後序遍歷:
F D B G H E C A
非遞迴 後序遍歷:
F D B G H E C A