计算机编程中最重要,最核心的一种数据结构:树。
树在日常生活中是广泛存在的,例如家族的族谱,各种公司部门结构图等。在计算机里,树更是无处不在,你平时接触到的堆,红黑树,二叉查找树,并查集,线段树,后缀树,树状数组都是树。数据库中索引是B+树,编译器中的语法树也是一种树,操作系统中的文件系统大多设计成树的结构……还有太多太多了。所以说,树这种数据结构是程序的灵魂,是程序的根基,是电,是光,是……总之,能不能把树这种数据结构玩得溜,可以说是做程序员的第一道坎。
树这种数据结构,最经典,最基础的实例就是二叉树,掌握好二叉树是学习其他高级数据结构的基石,所以,请大家一定要反复看这一节,反复写程序。真的,二叉树可能比你想象的还要重要得多。
JDK中的TreeMap是一种用树实现的Map,但是,它的实现是红黑树。虽然它本质上仍然是一棵二叉树,但却无论如何都得算是高级数据结构了(我敢打赌,90%以上的程序员无法完整地写出红黑树的实现,不要说删除节点,恐怕光是插入就写不全)。所以,我强烈建议大家在完全地掌握了二叉树和AVL树之前,先不要急着去翻JDK的源代码。
二叉树
二叉树是一种树型结构,它的特点是每个结点至多只有两棵子树,并且,二叉树的子树有左右之分,其次序不能任意颠倒。
先上代码,二叉树的结点定义:
class Node {
public Object data;
public Node left;
public Node right;
}
可以看到,我们除了定义了一个结点的左孩子和右孩子,还定义一个data变量用于存储数据。我们写一段代码,来实际地创建一棵二叉树:
public class Main {
public static void main(String args[]) {
Node a = new Node(Integer.valueOf(1));
Node b = new Node(Integer.valueOf(2));
Node c = new Node(Integer.valueOf(3));
a.left = b;
a.right = c;
}
}
class Node {
public Object data;
public Node left;
public Node right;
public Node(Object d) {
this.data = d;
}
}
我们通过这种方式,手动地创建了一个二叉树,这个二叉树,如果使用图画出来,就是以下的样子:
我们称 1 是父节点,2 是左孩子结点,3 是右孩子结点。如果一个节点没有子结点,例如图中的 2 和 3 ,那这个节点也是叶子节点。如果一个结点有子结点,也可以称其为内部结点,或者是非叶子结点。
树的层次是这样定义的,把树按照上图所示的情况画出来,1 位于第一层,2 和 3 位于第二层。树中结点的最大层次称为树的高度,2 和 3 位于第二层,所以这棵树的高度就是 2。再练习一下,下面这幅图中,树有多少层?每层几个结点?树的高度是多少?共有多少个叶子结点?多少个非叶子结点?
答案是:3层,第一层一个结点是 1, 第二层两个结点是2 和 3,第三层两个结点 4 和 5;树中结点的最大层数是3,所以树的高度就是3;共有三个叶子结点,分别是2, 4, 5;两个非叶结点,1和3。你答对了吗?
二叉查找树
二叉查找树的规则是,对于树中的任意一个结点,都满足,它的左孩子上的所有结点的值都比该结点小,而它的右孩子上的所有结点的值都比该结点大。与该结点值相同的的结点可以放在左孩子上,也可以放在右孩子上,这个可以根据实际情况灵活实现。
根据这个定义,二叉查找树的插入过程就是递进地判断待插入数据与树中结点值的大小关系,找到待插入数据应该出现的位置。
如果待插入数据比根结点的数据小,就去检查根结点的左孩子。如果左孩子不为空,就继续向下检查,如果左孩子为空,就说明已经找到应该插入数据的位置了。向右的情况则与向左的情况互为镜像,只是条件判断的符号换了一下而已。
class BinarySearchTree<T extends Comparable<T>> {
public Node root;
public boolean insert(T i) {
if (root == null) {
root = new Node(i);
return true;
}
Node current = root;
while (true) {
// 如果 i 比当前结点的值小
if (i.compareTo((T) current.data) < 0) {
if (current.left != null) {
current = current.left;
} else {
current.left = new Node(i);
break;
}
} else {
if (current.right != null)
current = current.right;
else {
current.right = new Node(i);
break;
}
}
}
return true;
}
}
在二叉查找树中找一个数据的思路与插入数据的思路很相似,唯一的不同之处是遇到空指针就是查找失败,说明待查找数据没在树中。程序不再列出,请读者自行实现。
有帮助到你的点赞、收藏和关注一下吧
需要更多教程,微信扫码即可
👆👆👆
别忘了扫码领资料哦【高清Java学习路线图】
和【全套学习视频及配套资料】