举个现实生活中的例子,公司的组织架构以下: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 --☺