二叉树定义:二叉树是每个结点最多有两个子树的树结构。
二叉树的特点:
- 每个节点最多有两个子节点,即二叉树不存在大于2的节点;
- 二叉树的子树有左右之分,其子树的次序不能颠倒。
二叉树的结构:
二叉树的性质:
- 在非空二叉树中,第 i 层的节点总数不超过 2 i-1,i >= 1;
- 深度为 h 的二叉树最多有 2 h - 1 个节点(h>=1),最少有 h 个节点;
- 对于任意一棵二叉树,如果其叶节点总数为 n1,而度数为2的节点总数为 n2,则 n1 = n2+1;
- 具有 n 个节点的完全二叉树的深度为 (Log2n) +1;
- 对于任意一个根节点,如果根节点的下标为n,那么它的左节点下标为 2n+1,右节点下标为 2n+2,根据这点可以很方便的用数组来保存二叉树的节点。
实现二叉树:
import java.util.ArrayList;
import java.util.List;
class BinaryTree<E> {
// 根节点
private Node<E> _root;
// 用列表暂时保存下节点
private List<Node<E>> nodes = new ArrayList<>();
public void createTree(E[] array) {
if (array.length == 0) {
return;
}
for (E e : array) {
nodes.add(new Node<>(null, null, null, e));
}
// 获取根节点
if (nodes.get(0) != null) {
_root = nodes.get(0);
}
// 根据二叉树的特性5来确定每个节点的左右子节点
for (int i = 0; i < nodes.size(); i++) {
if ((i * 2 + 1) < nodes.size() && nodes.get(i * 2 + 1) != null) {
nodes.get(i).left = nodes.get(i * 2 + 1);
}
if ((i * 2 + 2) < nodes.size() && nodes.get(i * 2 + 2) != null) {
nodes.get(i).right = nodes.get(i * 2 + 2);
}
}
}
// 节点类
static class Node<E> {
// 父节点暂时没有用到
Node<E> parent;
// 左节点
Node<E> left;
// 右节点
Node<E> right;
// 节点中存储的数据
E data;
Node(Node<E> parent, Node<E> left, Node<E> right, E data) {
this.data = data;
this.parent = parent;
this.left = left;
this.right = right;
}
@Override
public String toString() {
return data.toString();
}
}
public static void main(String[] args) {
Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8};
BinaryTree<Integer> binaryTree = new BinaryTree<>();
binaryTree.createTree(array);
}
}
实现二叉树的代码很简单,主要就是通过上述的性质5来确定每一个节点的左右子节点,一层一层的绑定关系,最终就可以将所有的节点联系完整。下面是二叉树生成的整个顺序:
二叉树的遍历:
前面我们生成了一棵二叉树,虽然生成了,但是却不知道如果去遍历二叉树输出每个节点对应的数据,那就等于拿到了某件东西却不会使用,这也是不希望看到的一种情况,下面就看看如果遍历一棵二叉树吧!
-
先序遍历: 首先访问根节点,这也就是为什么上面创建二叉树的时候要保存根节点的用处,根节点访问之后在访问其左子树,最后再访问右子树;在访问左右子树的时候依旧是按照先根然左后右的顺序。文字想必大家都看的模棱两可的,下面看顺序图吧,更直观,更清晰:
以上就是整个先序遍历的流程,这九个步骤大家应该一眼就能看的懂,用代码更加容易实现:
/**
* 先序遍历,访问根节点的操作发生在遍历其左右节点之前
*
* @param node 某个根节点
*/
public void preOrder(Node<E> node) {
if (node == null) {
return;
}
System.out.print(node + " ");
preOrder(node.left);
preOrder(node.right);
}
- 中序遍历:首先访问根节点的左子树,直到左子树访问完,回过头来访问根节点,最后访问左子树,这里便不在贴流程图了,简单的把实现代码贴出来:
/**
* 中序遍历,访问根节点的操作发生在遍历左右节点中间
*
* @param node 某个根节点
*/
public void inOrder(Node<E> node) {
if (node == null) {
return;
}
inOrder(node.left);
System.out.print(node + " ");
inOrder(node.right);
}
- 后序遍历:首先访问根节点的左子树,直到左子树访问完,回到根节点的右子树,访问完,最后访问根节点,照样贴上实现代码:
/**
* 后序遍历,访问根节点的操作发生在遍历左右节点之后
*
* @param node 某个根节点
*/
public void postOrder(Node<E> node) {
if (node == null) {
return;
}
postOrder(node.left);
postOrder(node.right);
System.out.print(node + " ");
}
至此为止,二叉树的内容就结束了,整个的源码可以扫描下方的二维码,我会将源码奉上,切记多敲几遍,这样就才能在脑海中加深印象,不然看过觉得理解了,三天之后就又抛之脑后了!
如果本文章你发现有不正确或者不足之处,欢迎你在下方留言或者扫描下方的二维码留言也可!