1. 樹的簡介
1.1 樹的定義
樹是一種特殊的數據結構,它可以用來描述有分支的結構,是由一個或一個以上的節點所組成的有限集合。
1.2 樹的特點
a. 存在一個特殊的節點,稱爲根節點;
b. 沒有父節點的節點稱爲根節點;
c. 每一個非根節點有且只有一個父節點;
d. 除了根節點外,每個子節點可以分爲多個不相交的子樹;
圖中A節點就稱爲根節點,B、C、D 均爲A的子節點。
1.3 樹的專有名詞
a. 樹根或者根節點(root):沒有父節點的節點稱爲根節點,如下圖中樹的根節點爲 A。
b. 父節點(parent):每一個節點的上層節點爲父節點,如下圖中 B 的父節點爲 A,E 的父節點爲 C。
c. 子節點(children):每一個節點的下層節點爲子節點,如 A的子節點有 B、C、D,而 C 的子節點有 E、F、G。
d. 兄弟節點(siblings):有共同父節點的節點爲兄弟節點,如 B、C、D 的父節點均爲 A,所以彼此稱爲兄弟節點。
e. 度(degree):子樹的個數稱爲該節點的度,如 A的度爲3,D 的度爲1。
f. 葉子節點或終端節點(terminal node):沒有子節點的節點,即度爲 0 的節點,如圖中的 B、E、F、G、I。
g. 階層或級(level):根節點的階層爲 1,圖中 A的階層爲 1,B、C、D的階層爲 2,E、F、G、H的階層爲3,I 的階層爲4。
h. 高度(height):樹的最大階層,例如此樹的高度爲 4。
i. 森林(forest):森林是由 n 個不相交的樹組成,移去樹根即爲森林。如圖中,移除節點 A,則包含三個樹的森林。
2. 二叉樹
2.1 二叉樹的定義
二叉樹是由有限個節點所組成的集合,此集合可以爲空集合,或由一個樹根及其左右兩個子樹所組成。簡單的說,二叉樹最多隻能有兩個子節點,就是度小於或者等於2。二叉樹的數據結構如下:
2.2 二叉樹的性質
1. 二叉樹第 i 層上的節點數目最多爲 ,其中 i ≥ 1。
2. 深度爲 k 的二叉樹至多有 -1個節點,其中 k ≥ 1。
2.3. 滿二叉樹
定義
高度爲 h,並且由 2^h - 1 個節點的二叉樹,被稱爲滿二叉樹。如下圖所示,這是一棵滿二叉樹
2.4. 完全二叉樹
定義
一棵二叉樹中,只有最下層節點的度可以小於2,並且最下層的葉節點集中在靠左的若干位置上,這樣的二叉樹稱爲完全二叉樹。
特點
葉子節點只能出現在最下層和次下層,且最下層的葉子節點集中在樹的左部。顯然,一棵滿二叉樹必定是一棵完全二叉樹,而完全二叉樹未必是滿二叉樹。如下圖,這是一棵完全二叉樹:
而下圖不是一棵完全二叉樹:
2.5 二叉查找樹
定義
二叉查找樹,又稱爲二叉搜索樹。假設 x 爲二叉查找樹中的一個節點,x 節點包含關鍵字 key,節點的 key 值記爲 key[x]。如果 y 是 x 的左子樹中的一個節點,則 key[y] ≤ key[x] ;如果 y 是 x 的右子樹的一個節點,則 key[y] ≥ key[x]。如下圖所示:
特點
a. 每個樹根的值需大於左子樹的值。
b. 每一個樹根的值需小於右子樹的值。
c. 左右子樹也是二叉搜索樹。
d. 沒有鍵值相等的節點。
3. 二叉樹存儲
3.1 數組表示法
如果要使用一維數組來存儲二叉樹,首先將二叉樹想象成一個滿二叉樹,而且第 k 個階層具有 個節點,並且按照順序存放在此一維數組之中。這裏從數組下標爲1的位置開始存放。
如下圖所示的二叉樹,用數組表示爲:{0,13,65,5,97,25,0,37,22,0,4,28,0,0,32,0}。
3.2 鏈表表示法
樹的節點:
package zzw.cn.tree;
/**
* @author 鷺島猥瑣男
* @create 2019/8/24 21:37
*/
public class TreeNode
{
int value;//節點值
TreeNode leftNode;//指向左節點
TreeNode rightNode;//指向右節點
public TreeNode(int value)
{
this.value = value;
}
}
二叉樹:
package zzw.cn.tree;
/**
* @author 鷺島猥瑣男
* @create 2019/8/25 0:40
*/
public class BinaryTree
{
TreeNode root;//根節點
public BinaryTree(int[] array)
{
root = transformArrayToBinaryTree(array, 1);
}
//採用遞歸方式創建二叉樹
public TreeNode transformArrayToBinaryTree(int[] array, int index)
{
if (index < array.length)
{
int value = array[index];
if (value != 0)
{
TreeNode node = new TreeNode(value);
array[index] = 0;
node.leftNode = transformArrayToBinaryTree(array, index * 2);
node.rightNode = transformArrayToBinaryTree(array, index * 2 + 1);
return node;
}
}
return null;
}
}
4. 二叉樹的遍歷
二叉樹的遍歷,就是訪問樹的所有節點各一次,並且在遍歷後,將樹中的數據轉化爲線性關係。樹的遍歷存在三種方式:
a. 中序遍歷
b. 前序遍歷
c. 後續遍歷
4.1 中序遍歷
若二叉樹非空,則執行以下操作:左子樹 → 樹根 → 右子樹。就是沿着樹的左子樹一直往下,直到無法前進後退回返回父節點,再往右子樹一直往下。如果右子樹也走完了就退回上層的左節點,再重複左、中、右的順序遍歷。
上圖樹的中序遍歷如下所示:
package zzw.cn.tree;
/**
* @author 鷺島猥瑣男
* @create 2019/8/25 1:18
*/
public class BinaryTreeTest
{
public static void main(String[] args)
{
int[] arr = {0, 13, 65, 5, 97, 25, 0, 37, 22, 0, 4, 28, 0, 0, 32, 0};
BinaryTree tree = new BinaryTree(arr);
inOrder(tree);//中序遍歷
}
//中序遍歷
private static void inOrder(TreeNode node)
{
if (node != null)
{
inOrder(node.leftNode);
System.out.print(node.value + " ");
inOrder(node.rightNode);
}
}
private static void inOrder(BinaryTree tree)
{
System.out.print("中序遍歷:");
inOrder(tree.root);
}
}
結果:
中序遍歷:22 97 65 4 25 28 13 5 32 37
4.2 前序遍歷
前序遍歷的順序爲:樹根 → 左子樹 → 右子樹。前序遍歷就是從根節點開始處理,根節點處理完往左子樹,直到無法前進再處理右子樹。
上圖樹的前序遍歷如下所示:
package zzw.cn.tree;
/**
* @author 鷺島猥瑣男
* @create 2019/8/25 1:18
*/
public class BinaryTreeTest
{
public static void main(String[] args)
{
int[] arr = {0, 13, 65, 5, 97, 25, 0, 37, 22, 0, 4, 28, 0, 0, 32, 0};
BinaryTree tree = new BinaryTree(arr);
preOrder(tree);//前序遍歷
}
//前序遍歷
private static void preOrder(TreeNode node)
{
if (node != null)
{
System.out.print(node.value + " ");
preOrder(node.leftNode);
preOrder(node.rightNode);
}
}
private static void preOrder(BinaryTree tree)
{
System.out.print("前序遍歷:");
preOrder(tree.root);
}
}
結果:
前序遍歷:13 65 97 22 25 4 28 5 37 32
4.3 後序遍歷
後序遍歷的順序爲:左子樹 → 右子樹 → 樹根。後序遍歷和前序遍歷的方法相反,它是把左子樹的節點和右節點都處理完了才處理樹根。
上圖樹的後序遍歷如下所示:
package zzw.cn.tree;
/**
* @author 鷺島猥瑣男
* @create 2019/8/25 1:18
*/
public class BinaryTreeTest
{
public static void main(String[] args)
{
int[] arr = {0, 13, 65, 5, 97, 25, 0, 37, 22, 0, 4, 28, 0, 0, 32, 0};
BinaryTree tree = new BinaryTree(arr);
postOrder(tree);//後序遍歷
}
//後序遍歷
private static void postOrder(TreeNode node)
{
if (node != null)
{
postOrder(node.leftNode);
postOrder(node.rightNode);
System.out.print(node.value + " ");
}
}
private static void postOrder(BinaryTree tree)
{
System.out.print("後序遍歷:");
postOrder(tree.root);
}
}
結果:
後序遍歷:22 97 4 28 25 65 32 37 5 13
參考:
https://github.com/LRH1993/android_interview/blob/master/data-structure/tree/tree-introduction.md
《圖解數據結構-使用Java》