舉個現實生活中的例子,公司的組織架構以下:node
# |ceo|
# / \
# |cto| |cfo|
# / \ / \
# |se| |se| |sm| |sm|
複製代碼
經過上圖能夠獲得一個直觀的感覺,就是明顯的層級
關係。從頂端的 CEO ,到僱傭各類職能人員組成公司,就像一棵 「樹」 (顛倒的),從根部開始生長,到開枝散葉。在開發中多少都接觸過樹的概念 ,做爲一種數據結構被普遍得應用在各類計算機語言中,瞭解它的設計思想對往後的開發是很是有幫助的。以上圖公司的組織架構來了解下經常使用的術語,都比較基礎:bash
二叉樹是一種特殊的樹 ,每一個節點最多隻能有兩個子節點(左子節點、右子節點):數據結構
從上圖能夠看出二叉樹是一組節點的集合,每一個節點有 3 個屬性:架構
捋清楚這點關係後就能夠動手敲代碼了,從最簡單的開始:ide
public class BinaryTree {
int value;
BinaryTree left;
BinaryTree right;
public BinaryTree(int value) {
this.value = value;
this.left = null;
this.right = null;
}
@Override
public String toString() {
return "value: " + value + " left: " + left + " right: " + right;
}
}
複製代碼
接下來嘗試往二叉樹中插入節點,分別定義插入左子節點和右子節點的方法。規則以下post
第一點比較好理解,第二點畫個圖:測試
也很好理解,思路有了,代碼就比較好寫了, BinaryTree 類中添加以下方法:ui
public void insertLeft(int value) {
if (this.left == null) {
this.left = new BinaryTree(value);
} else {
BinaryTree newNode = new BinaryTree(value);
newNode.left = this.left;
this.left = newNode;
}
}
public void insertRight(int value) {
if (this.right == null) {
this.right = new BinaryTree(value);
} else {
BinaryTree newNode = new BinaryTree(value);
newNode.right = this.right;
this.right = newNode;
}
}
複製代碼
測試下上面寫的代碼,假設要生成的二叉樹以下this
測試:spa
BinaryTree node11 = new BinaryTree(11);
node11.insertLeft(8);
node11.insertRight(16);
BinaryTree node8 = node11.left;
BinaryTree node16 = node11.right;
node8.insertLeft(5);
node8.insertRight(10);
node16.insertRight(18);
BinaryTree node5 = node8.left;
BinaryTree node10 = node8.right;
BinaryTree node18 = node16.right;
System.out.println(node11.value);//11
System.out.println(node8.value);//8
System.out.println(node16.value);//16
System.out.println(node5.value);//5
System.out.println(node10.value);//10
System.out.println(node18.value);//18
複製代碼
二叉樹的遍歷分爲深度優先遍歷(Depth-First Search)和廣度優先遍歷 (Breadth-First Search)。
代碼實現:
public static void preOrder(BinaryTree currentNode) {
System.out.println(currentNode.value);
if (currentNode.left != null) {
preOrder(currentNode.left);
}
if (currentNode.right != null) {
preOrder(currentNode.right);
}
}
複製代碼
代碼實現:
public static void inOrder(BinaryTree currentNode) {
if (currentNode.left != null) {
inOrder(currentNode.left);
}
System.out.println(currentNode.value);
if (currentNode.right != null) {
inOrder(currentNode.right);
}
}
複製代碼
代碼實現:
public static void postOrder(BinaryTree currentNode) {
if (currentNode.left != null) {
postOrder(currentNode.left);
}
if (currentNode.right != null) {
postOrder(currentNode.right);
}
System.out.println(currentNode.value);
}
複製代碼
代碼實現:
public static void bfs(BinaryTree currentNode) {
Queue<BinaryTree> queue = new LinkedList<>();
if (currentNode == null) {
return;
}
queue.clear();
queue.add(currentNode);
while (!queue.isEmpty()) {
BinaryTree node = queue.remove();
System.out.println(node.value);
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
}
複製代碼
這裏藉助隊列來實現廣度優先遍歷,假如要遍歷下面這棵二叉樹:
整個流程以下:
二叉查找樹就是排序後的二叉樹,通俗理解以下:
左子樹上全部節點的值 < 根節點的值 < 右子樹上全部節點的值
複製代碼
假如要按照 50,76,21,4,32,100,64,52 順序生成一顆樹,流程以下
代碼以下:
public static BinaryTree insert(BinaryTree currentNode, int value) {
if (currentNode == null) {
return new BinaryTree(value);
} else if (value < currentNode.value) {
currentNode.left = insert(currentNode.left, value);
} else if (value > currentNode.value) {
currentNode.right = insert(currentNode.right, value);
}
return currentNode;
}
複製代碼
在上面例子的基礎上,假設要查找 52 這個值是否存在,流程以下:
代碼以下:
public static boolean find(BinaryTree currentNode, int value) {
if (currentNode == null) {
return false;
}
if (value < currentNode.value) {
return find(currentNode.left, value);
}
if (value > currentNode.value) {
return find(currentNode.right, value);
}
return true;
}
複製代碼
刪除操做相對繁瑣,須要考慮幾種狀況:
# |50| |50|
# / \ / \
# |30| |70| (DELETE 20) ---> |30| |70|
# / \ \
# |20| |40| |40|
複製代碼
這種狀況很簡單,直接刪除便可
# |50| |50|
# / \ / \
# |30| |70| (DELETE 30) ---> |20| |70|
# /
# |20|
複製代碼
這種狀況也相對簡單,做爲 20 父節點的 30 被刪除了,那麼此時 20 就由 30 的父節點 50 來 「接管」。
# |50| |50|
# / \ / \
# |30| |70| (DELETE 30) ---> |40| |70|
# / \ /
# |20| |40| |20|
複製代碼
這種狀況稍微麻煩一些,首先查找節點 30 的右子樹中最小的值(40),並用它替換節點 30 的值,再將節點 40 刪掉
。不太好理解?下面會有更詳細的分解介紹。
代碼以下:
public static BinaryTree delete(BinaryTree currentNode, int value) {
if (currentNode == null) {
return null;
}
if (value < currentNode.value) {
currentNode.left = delete(currentNode.left, value);
} else if (value > currentNode.value) {
currentNode.right = delete(currentNode.right, value);
} else {
if (currentNode.left == null && currentNode.right == null) {
System.out.println("deleting leaf node" + value);
return null;
} else if (currentNode.left == null) {
System.out.println("no left node; deleting " + value);
return currentNode.right;
} else if (currentNode.right == null) {
System.out.println("no right node; deleting " + value);
return currentNode.left;
} else {
currentNode.value = minimumValue(currentNode.right);
delete(currentNode.right, currentNode.value);
System.out.println("with two child node; deleting " + value);
}
}
return currentNode;
}
public static int minimumValue(BinaryTree root) {
if (root.left != null) {
return minimumValue(root.left);
}
return root.value;
}
複製代碼
作個測試,經過下面代碼生成一棵二叉查找樹:
BinaryTree root = new BinaryTree(15);
insert(root, 10);
insert(root, 20);
insert(root, 8);
insert(root, 12);
insert(root, 17);
insert(root, 25);
insert(root, 19);
bfs(root);
# |15|
# / \
# |10| |20|
# / \ / \
# |8| |12| |17| |25|
# \
# |19|
複製代碼
刪無子節點的節點 8 :
delete(root, 8);
bfs(root);
# |15|
# / \
# |10| |20|
# \ / \
# |12| |17| |25|
# \
# |19|
複製代碼
再刪帶一個子節點的節點 17:
delete(root, 17);
bfs(root);
# |15|
# / \
# |10| |20|
# \ / \
# |12| |19| |25|
複製代碼
再刪帶兩個子節點的節點 15:
delete(root, 15);
bfs(root);
# |19|
# / \
# |10| |20|
# \ \
# |12| |25|
複製代碼
刪帶兩個子節點的節點的過程用流程表示:
Enjoy --☺