概述
二叉樹,是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。
二叉樹的特性
- 在非空二叉樹中,第i層的結點總數不超過 , i>=1;
- 深度爲h的二叉樹最多有 個結點(h>=1),最少有h個結點;
- 對於任意一棵二叉樹,如果其葉結點數爲N0,而度數爲2的結點總數爲N2,則N0=N2+1;
- 具有n個結點的完全二叉樹的深度爲
- 有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,則無右兒子。 - 給定N個節點,能構成h(N)種不同的二叉樹。h(N)爲卡特蘭數的第N項。h(n)=C(2*n,n)/(n+1)。
- 設有i個枝點,I爲所有枝點的道路長度總和,J爲葉的道路長度總和J=I+2i[4]
二叉樹的分類
斜樹
所有的結點都只有左子樹的二叉樹叫左斜樹。所有結點都是隻有右子樹的二叉樹叫右斜樹。這兩者統稱爲斜樹。所以,線性表的數據結構可以理解成樹的一種表達方式。
滿二叉樹
在一棵樹中,如果所有的分支節點都存在左子樹和右子樹
,並且所有的葉子節點都在同一層上,那麼這樣的二叉樹稱之爲滿二叉樹。
完全二叉樹
對一顆具有n個節點的二叉樹按層序編號,如果編號爲i(1<= i <=n)的節點與同樣深度的滿二叉樹中編號爲i的節點的位置完全相同,那麼這樣的樹,稱之爲完全二叉樹。
二叉樹的存儲結構
順序存儲結構:
二叉鏈表:
二叉樹的遍歷
二叉樹的數據結構中,常用的遍歷方式有三種:
- 先序遍歷
- 中序遍歷
- 後序遍歷
當然,二叉樹還有一種不常見的遍歷方式-層序遍歷,這裏暫不做介紹。
在樹的遍歷中,我們通常使用的是通過遞歸方式來進行遍歷。
先序遍歷
規則是若二叉樹爲空,則空操作返回,否則先訪問跟結點,然後先序遍歷左子樹,再先序遍歷右子樹。
代碼實現如下:
/**
* 先序遍歷
*
* parent->left->right
*/
public void preOrder(TreeNode node) {
if (node == null) {
return;
}
System.out.print(node.data + "\t");
preOrder(node.leftChild);
preOrder(node.rightChild);
}
中序遍歷
規則是若樹爲空,則空操作返回,否則從根結點開始(注意並不是先訪問根結點),中序遍歷根結點的左子樹,然後是訪問根結點,最後中序遍歷右子樹。
代碼實現如下:
/**
* 中序遍歷
*
* left->parent->right
*/
public void midOrder(TreeNode node) {
if (node == null) {
return;
}
midOrder(node.leftChild);
System.out.print(node.data + "\t");
midOrder(node.rightChild);
}
後序遍歷
規則是若樹爲空,則空操作返回,否則從左到右先葉子後結點的方式遍歷訪問左右子樹,最後是訪問根結點。
代碼實現如下:
/**
* 後序遍歷
*
* left->right->parent
*/
public void postOrder(TreeNode node) {
if (node == null) {
return;
}
postOrder(node.leftChild);
postOrder(node.rightChild);
System.out.print(node.data + "\t");
}
Java實現二叉樹
package structdemo;
/**
*
* 二叉樹
*
* @author zhangke
*/
public class BinaryTree {
// 創建二叉樹並調用遍歷方法
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
binaryTree.createBinaryTree();
System.out.println("height:" + binaryTree.getHeight());
System.out.println("size:" + binaryTree.getSize());
binaryTree.preOrder(binaryTree.root);
binaryTree.midOrder(binaryTree.root);
binaryTree.postOrder(binaryTree.root);
}
/**
* 根節點
*/
private TreeNode root = null;
public BinaryTree() {
root = new TreeNode(1, "A");
}
/**
* 構建二叉樹
*
* A
*
* B C
*
* D E F
*/
public void createBinaryTree() {
TreeNode nodeB = new TreeNode(2, "B");
TreeNode nodeC = new TreeNode(3, "C");
TreeNode nodeD = new TreeNode(4, "D");
TreeNode nodeE = new TreeNode(5, "E");
TreeNode nodeF = new TreeNode(6, "F");
root.leftChild = nodeB;
root.rightChild = nodeC;
nodeB.leftChild = nodeD;
nodeB.rightChild = nodeE;
nodeC.rightChild = nodeF;
}
/**
* 獲取樹的深度
*
* @return
*/
public int getHeight() {
return getHeight(root);
}
/**
* 獲取指定結點的深度
*/
private int getHeight(TreeNode node) {
if (node == null) {
return 0;
}
// 每當一個結點存在子結點時,Height就加一個
int i = getHeight(node.leftChild);
int j = getHeight(node.rightChild);
return (i > j) ? (i + 1) : (j + 1);
}
/**
* 獲取結點個數
*
* @return
*/
public int getSize() {
return getSize(root);
}
private int getSize(TreeNode node) {
if (node == null) {
return 0;
}
return 1 + getSize(node.leftChild) + getSize(node.rightChild);
}
/**
* 先序遍歷
*
* parent->left->right
*/
public void preOrder(TreeNode node) {
if (node == null) {
return;
}
System.out.print(node.data + "\t");
preOrder(node.leftChild);
preOrder(node.rightChild);
}
/**
* 中序遍歷
*
* left->parent->right
*/
public void midOrder(TreeNode node) {
if (node == null) {
return;
}
midOrder(node.leftChild);
System.out.print(node.data + "\t");
midOrder(node.rightChild);
}
/**
* 後序遍歷
*
* left->right->parent
*/
public void postOrder(TreeNode node) {
if (node == null) {
return;
}
postOrder(node.leftChild);
postOrder(node.rightChild);
System.out.print(node.data + "\t");
}
/**
* 定義樹的結點
*
* @author zhangke
*/
public class TreeNode {
/**
* 下標
*/
private int index;
/**
* 數據
*/
private String data;
/**
* 左孩子
*/
private TreeNode leftChild;
/**
* 右孩子
*/
private TreeNode rightChild;
public TreeNode(int index, String data) {
this.index = index;
this.data = data;
}
}
}
常見面試題
已知,某樹的先序遍歷爲:{ 4, 2, 1 ,0, 3, 5, 9, 7, 6, 8 },中序遍歷爲: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },請畫出該樹。
在解題之前我們可以明確一個概念:二叉樹中序遍歷加任意一種其他遍歷方式都可以確定一棵樹。
解釋:因爲不論是先序遍歷、後序遍歷或者是層序遍歷,都只能確定一顆樹的根節點,在確定根節點後通過中序遍歷就能區分樹的左右子樹,這樣通過遞歸的思想就能確定一棵樹了。
解題步驟如下: