首先告诉大家我写这篇博客的目的,大三寒假在家准备CCF,想着先复习一下基础的数据结构,但是一个人自学实在是比较难以坚持,你懂的,前几天在看软考相关的文章时看到一个思想觉得很好:以考促学。那我就以写博客促学吧!
在这篇文章之后,会写一下 二叉排序树(BST) 的改进(平衡二叉树等),因为任何算法都不是完美的。
本篇博客的内容:
BST的定义;插入;查找;删除
行文中一些素材来源:
https://www.cnblogs.com/Kiven5197/p/8877177.html
https://blog.csdn.net/isunbin/article/details/81703762
大家也可以去看看这两篇,流程图片比较详细
如何汲取本博客:
在阅读之前要想一想自己想要弄明白什么,自己的困惑是什么(对一般人来说是删除)
各取所需,重点有加粗标注
0 为什么要引入二叉排序树
既然有了线性存储排序算法(顺序存储和链式存储),为什么还要引入bst?
肯定是因为有不足啊,其实各种排序算法各有优缺点,适用于不同场合。
下面聊聊不足:
- 顺序存储
无序的:查找插入删除都困难
有序的:可以二分查找,效率还行,但插入删除可能会造成大量数据移动,导致效率不高 - 链式存储
插入删除还可,但无论是否排序,查找操作都比较麻烦,需要从头向后对比
1 定义
要点:左<根<右
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。
也就是说,左<根<右,如下图:
2 二叉排序树的创建与插入
创建要点:二叉排序树类要包含一个根节点
插入要点:大放右,小放左
创建与插入过程如下图所示:
要点:若二叉树为空,则首先单独生成根节点;新插入的节点总是叶子节点。
3 二叉排序树的查找
要点:递归(其实树这种数据结构,很多地方都用到了递归)
步骤:
- 若根结点的值等于查找的值,成功。
- 若小于根结点的值,递归查左子树。
- 若大于根结点的值,递归查右子树。
- 若子树为空,查找不成功。
代码如下:
public Node search(int value) {
if(this.value==value) {
return this;
}else if(value<this.value) {
if(left==null) {
return null;
}
return left.search(value);
}else{
if(right==null) {
return null;
}
return right.search(value);
}
}
4 二叉排序树节点的删除
先说:删除就是指针置空
这才是二叉排序树中最重要的部分,说重要是因为相比于查找和插入,节点删除是比较麻烦的,有三种情况。
不过复杂一点才能更好地锻炼数据结构与算法思维。
下面分情况讨论
4.1 叶子节点的删除(直接删除)
- 该叶子节点也是根节点(这是一颗只有一个节点的二叉树),将该节点指针置为null即可
- 将父节点连接该删除节点的指针置空即可
//parent指的是该节点的父节点
//要删除的节点是父节点的左子节点
if(parent.left.value==value) {
parent.left=null;
//要删除的节点是父节点的右子节点
}else {
parent.right=null;
}
4.2 只有左子树或只有右子树的树节点的删除(上移子树)
//有左子节点
if(target.left!=null) {
//要删除的节点是父节点的左子节点
if(parent.left.value==value) {
parent.left=target.left;
//要删除的节点是父节点的右子节点
}else {
parent.right=target.left;
}
//有右子节点
}else {
//要删除的节点是父节点的左子节点
if(parent.left.value==value) {
parent.left=target.right;
//要删除的节点是父节点的右子节点
}else {
parent.right=target.right;
}
}
4.3 既有左子树也有右子树的树节点的删除(用AB两个树节点之一来替换当前节点,然后调整树结构)
也就是说,用当前节点的左子树中值最大的节点(A)替换当前节点,或者用当前节点的右子树中值最小的节点(B)替换当前节点。然后调整树结构(说是调整树结构,其实搜索到A(B)后 删除 A(B)就可以了,因为删除本身就会调整,也就是会重新分三种情况删除)
if(target.left!=null&&target.right!=null) {
//删除右子树中值最小的节点,获取到该节点的值(或者删除左子树中值最大的节点)
int min = deleteMin(target.right);
//替换目标节点中的值
target.value=min;
}
其中deleteMin函数的作用是删除右子树中值最小的节点
下面给出该函数代码:
private int deleteMin(Node node) {
Node target = node;
//递归向左找
while(target.left!=null) {
target=target.left;
}
//删除最小的这个节点
delete(target.value);
return target.value;
}
总结
二叉排序树整挺好
如果有需求,之后会更新一下把整个源码链接贴在下面,祝各位精通算法