二叉搜索树
概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树
:
-
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
-
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
-
它的左右子树也分别为二叉搜索树
代码实现
结点静态内部类
public class BinarySearchTree {
static class TreeNode {
public int key;
public TreeNode left;
public TreeNode right;
public TreeNode(int key) {
this.key = key;
}
}
public TreeNode root;
}
插入-- insert
public boolean insert(int key) {
if(this.root == null) {
this.root = new TreeNode(key);
return true;
}
TreeNode cur = this.root;
TreeNode parent = null;
//找到插入位置的父亲
while(cur != null) {
if(cur.key < key) {
parent = cur;
cur = cur.right;//往右走
} else if(cur.key > key) {
parent = cur;
cur = cur.left;//往左走
} else {
//不能插入相同的元素
return false;
}
}
TreeNode node = new TreeNode(key);
if(key > parent.key) {
parent.right = node;
} else {
parent.left = node;
}
return true;
}
图解
查找-- search
public TreeNode search(int key) {
TreeNode cur = root;
while(cur != null) {
if(cur.key > key) {
cur = cur.left;
} else if(cur.key < key) {
cur = cur.right;
} else {
return cur;
}
}
return null;
}
查找函数相对比较简单,就是个二分查找。
删除-- remove (难点)
public boolean remove(int key) {
TreeNode cur = root;
TreeNode parent = null;
while(cur != null) {
if(cur.key > key) {
parent = cur;
cur = cur.left;
} else if(cur.key < key) {
parent = cur;
cur = cur.right;
} else {
removeNode(parent, cur);
return true;
}
}
return false;
}
private void removeNode(TreeNode parent, TreeNode cur) {
//1.左为空 2.右为空 3.都不为空
if(cur.left == null) {
if(cur == root) {
root = cur.right;
} else if(cur == parent.right) {
parent.right = cur.right;
} else {
parent.left = cur.right;
}
} else if(cur.right == null) {
if(cur == root) {
root = cur.left;
}else if(cur == parent.left) {
parent.left = cur.left;
} else {
parent.right = cur.left;
}
} else {
TreeNode targetParent = cur;
TreeNode target = cur.right;
//可以在待删除结点的左子树找一个最大值将其覆盖,也可以在待删除结点的右子树找一棵最小的值将其覆盖,最后删除替罪羊
while(target.left != null) {
targetParent = target;
target = target.left;
}
cur.key = target.key;
//判断替罪羊是targetParent的左孩子还是右孩子
if(target == targetParent.left) {
targetParent.left = target.right;
} else {
targetParent.right = target.right;
}
}
}
思路
分情况讨论:设待删除结点为 cur ,待删除结点为 parent 。
1.cur.left == null
- cur == root
- cur == parent.left
- ccur == parent.right
2.cur.right == null
- cur == root
- cur == parent.left;
- cur == parent.right
3.cur.left != null && cur.right != null
- 替换法(难点)
画图解释替换法!
情况二和情况一类似
替换法
替换法分析
当带删除元素key有左右孩子的时候,直接把key删除是不现实的,因为你根本不知到key下面的结构是什么,就算知道,调整起来也无从下手,所以我们需要借助替换法来实现,既可以选择key的左子树中的最大值进行替换,也可以选择key的右子树中最小值进行替换,因为这样才能保证二叉搜索树的结构不被破坏。
检查插入 / 删除后是否还是一棵二叉搜索树-- 中序遍历
下图中序遍历的结果为:0,1,2,3,4,5,6,7,8,9
代码实现
public List<Integer> inOrder(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root == null) {
return list;
}
List<Integer> left = inOrder(root.left);
list.addAll(left);//左
list.add(root.key);//中
List<Integer> right = inOrder(root.right);
list.addAll(right);//右
return list;
}
咱们在对二叉搜索树进行CURD操作时,就可以通过调用中序遍历的调试和打印来判断是否正确!
性能分析
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能!
![](https://i-blog.csdnimg.cn/blog_migrate/27e4c1368808eef7f472c09ad877e43f.png)
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:O(log2^N)
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:O(N)
本期博客就到这里了,下期再见!!!