1.写在前面
前面的博客,我简单的介绍的一维的结构,数组、链表、跳表,主要是一些一维的数据结构,今天我们来介绍一下,二维结构的数据,我们就先来简单的介绍下树的类型。
2.树、二叉树、二叉搜索树
由于链表的查找速度比较慢,所以我们想出了跳表的结构,同样的是一维的结构,但是我们可以通过升维的思想,来提高的链表的访问的速度。一般的链表的如下所示:
查找的速度慢,是因为我们每一次只能一个节点一个节点往下的访问,我们是不是可以通过增加next的个数来达到加速访问的节点的效果,于是就有了树,于是有了如下的结构:
如上图,树有个根节点A,然后B相对A就是儿子节点,然后BC就是兄弟节点,然后H、I、E、F、G就是这颗树的叶子节点。这就是一个简单的树的结构。那么什么是二叉树呢?就是在树的基础上规定了树只能有两个儿子节点,于是二叉树的结构就如下图所示:
那么讲到的升维,我们这儿必须要提及一下我们的图了,图和树的区别就是,图能形成一个闭环。所以这儿有一个很重要的话就是:链表是特殊的化的树,树是特殊化图。
那么什么是二叉搜索树呢?具体的看下图:
二叉搜索树,也称为二叉搜索树、有序二叉树、排序二叉树,是指一颗空树或者具有下列性质的二叉树:
- 左子树上所有节点的值均小于它的根节点的值;
- 右子树上所有节点的值均大于它的根节点的值;
- 依次类推:左、右子树也分别为二叉查找树。
- 中序遍历:升序排列
3.二叉树的例子
例如我们求解斐波那契数列的数列的时候,我们利用递归解决问题的时候,就是一个递归树,具体的如下:
还有一些围棋的程序,例如阿尔法狗,还有我们的人生也是一颗树,选择不同,人生的岔路口也是不一样的。
4.二叉树的定义的结构
那么二叉树的定义的结构如何呢?由于我是java的开发的程序员,所以这儿只展示java的数据格式定义,具体的如下:
private class TreeNode{
private int value;
private TreeNode left, right;
public TreeNode(int value){
this.value = value;
this.left = null;
this.right = null;
}
}
5.二叉树的遍历
- 前序(pre-order) 根-左-右
- 中序(in-order) 左-根-右
- 后序(post-order) 左-右-根
对应的实例代码如下:
def preorder(self, root):
if root:
self.traverse_path.append(root.val)
self.preorder(root.left)
self.preorder(root.right)
def inorder(self, root):
if root:
self.inorder(root.left)
self.traverse_path.append(root.val)
self.inorder(root.right)
def postorder(self, root):
if root:
self.postorder(root.left)
self.inorder(root.right)
self.traverse_path.append(root.val)
6.二叉搜索树的常见的操作
现在假设生成的二叉搜索树如下:
-
搜索的操作
比如我现在要查找’5’的值,这个时候第一步和根节点比较,发现5比15小,这个时候就会进入左边,然后和6比较,发现还是比6小,这个时候去左边,这个时候会和4比较,发现5比4大,这个时候就去4的右边找,这个时候发现5真好等于我们要查找的节点,于是就直接返回。具体的路径如下:
但是我们如果查找的值,不存在这个树中呢?比如我们要查找24,于是走来先和15比较,发现比15大,于是去右边去找,这个时候找到是23,然后发现24比23大,于是又去右边去找,这个时候找到的是71,发现24比71小,于是去左边找,这个时候是50,发现24比50小,这个时候去左边找,发现没有了,于是就是返回没有找到,具体的路径如下:
-
插入的操作
其实插入的操作和搜索的操作差不多,就是找到对应的位置,然后进行插入,我们还是选择上面生成的二叉搜索树,具体的如下:
假设我们现在插入一个3进去,走来3比15小,然后去左边,发现3比6小,于是又是左边,这个时候发现3还是4小,于是又是去左边,然后发现4的左边没有东西,于是就将3插入到这个位置,具体的路径如下:
-
删除的操作
删除的操作,可能会破坏二叉搜索树的结构,这个时候,我们就需要维护二叉搜索树的结构了,具体的树的结构,还是和原来的一样的具体的如下:
这个时候比如说,我们要删除5这个节点,这个时候我们还是和之前的查找的操作一样,先查找到5这个节点,然后删除,因为5是叶子节点,所以不需要变换树的结构,具体的如下:
如果我们删除的是6的节点,会发生什么情况呢?很显然有两种的方式,一种就是找到第一个比6大的节点作为这个根节点,另外一种就是找到第一个比6小的节点作为这个根节点。这儿我们采取的是第一种的方式,这儿删除6后,第一个比6大的节点就是7,于是7变成根节点,于是最终的树变成如下:
7.二叉搜索树的时间复杂度
搜索、插入、删除的时间的复杂度都是O(log(n)),但是也有一些比较特殊的情况,就是二叉搜索树退还成了链表的接口,具体的如下:
这样他的时间复杂度就和链表的是一样的了。
8.写在最后
这篇博客主要讲了一下树、二叉树、二叉搜索树的一些简单的概念,后面我会继续讲HashMap的1.8的源码。