一、什么是二叉排序树
二叉排序树(Binary Sort Tree)又称二叉查找树、二叉搜索树。 它或者是一棵空树;或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
当我们使用需要对数列进行操作的时候,我们原本有以下选择:
- 数组:不排序的数组插入快而查找慢,排序数组通过算法可以快速查找,但是插入效率又会受到影响
- 链表:不管是否有序,插入都快,但是查找效率都不高
- 哈希表:查找修改都简单,但当哈希冲突严重的时候传统哈希表效率也会下降
而二叉排序树的查找类似二分查找,而插入类似链表,相较以上三种结构可以较好的平衡查找和插入效率
二、代码实现
我们先实现一个节点类:
1 | /** |
再实现一个二叉排序树类:
1 | /** |
1.添加
思路如下:
- 获取要插入的节点与其父节点比较值,比父节点小向左树插,否则就向右插
- 插入是判断父节点的左/右子节点是否存在,存在就继续递归遍历左/右树直到找到插入位置,否则直接插入
在节点类中添加方法:
1 | /** |
2.查找
1 | /** |
3.删除
删除节点时会出现三种情况:
- 要删除的节点是叶子结点
- 要删除的节点是一棵树的根节点
- 要删除的节点是两棵树的根节点
不管对于哪种情况而言,我们都需要先找到要要删除节点的父节点的方法:
1 | /** |
我们先区分三种情况,然后在此基础上分别实现三种情况下的删除:
1 | /** |
我们在以上代码的基础上继续分析思路。
3.1删除的节点是叶子结点
即方法deleteLeafNode()
- 找到要删除的节点,并判断其左右子节点是否都为空
- 若都为空,再找到其父节点,然后判断要删除的节点是父节点的左子节点还是右子节点
1 | /** |
3.2删除的节点有一课子树
即方法deleteOneBranchNode()
- 找到目标节点,判断目标节点有的那颗子树是左子树还是右子树
- 判断目标节点是否为根节点,如果是就直接将根节点替换为目标节点的子节点
- 如果不是根节点,再判断目标节点是其父节点的左子节点还是右子节点
- 让父节点的子节点指向目标节点的子节点
1 | /** |
3.3 删除的节点结点有两颗子树
即方法deleteLeafNode()
当有要删除的节点有两颗子树时情况比较特殊,我们不能通过直接改变指针指向的方式让子树直接“移接”到目标节点的父节点上,我们需要在目标节点的子树中找到一个能替换目标节点并且不会改变排序树顺序的节点。
我们举个例子,现有{5,3,2,7,6,4,1,0,8},形成的树
我们要删除节点3,那3的位置就必须换成一个比3的右子树节点小而比左子树所有节点大的数,也就是说,这个数:
- 左树选最大:可以是目标节点的左子节点的左树最大值,也就是2;
- 右树选最小:可以是目标节点的右子节点的右树最小值,也就是4;
这里我们选择用右子树的最小值作为替换值:
1 | /** |
按以上方法,等于删除4,然后让3的值变为4:
三、完整代码
具体代码和测试用例可以去GitHub上看,这里就放实现类:
1 | /** |