作者
:学Java的冬瓜
博客主页
:☀冬瓜的主页
专栏
:【Java 数据结构与算法】
分享
:曾梦想仗剑走天涯,看一看世界的繁华。——许巍《曾经的你》
主要内容
:二叉搜索树的概念,二叉搜索树的查找、新增,保姆级别分析二叉搜索树的删除问题。
文章目录
- 一、概念理解
-
- 二、代码详解
-
- 1、定义节点结构
- 2、查找
- 2、新增
- 3、删除
-
- 4、如何简单判断代码是否正确?
一、概念理解
@ 什么是二叉搜索树?
- 二叉搜索树又叫二叉排序树,又叫二叉查找树。
- 二叉搜索树有可能是空树
- 不是空树时:
它的左子树上的所有节点都小于根节点
它的右子树上的所有节点都大于根节点
它的左右子树都分别是二叉搜索树。
那么下面我们用图示来理解:
二、代码详解
1、定义节点结构
- 定义根节点root。
- 使用内部类定义节点,为了方便操作,我直接把val定义成public,本来应该是用set和get方法来操作的。
- 注意:查找,新增,删除的方法都放在BinarySearchTree类里面。因为解决了查找相当于解决了修改了,所以修改就不再示例。
- 注意:插入、删除操作都需要先查找。如果该数是平衡的,那查找效率为logn,如果恰好n个节点有n层,那效率为o(n)。AVL树和红黑树都是平衡二叉搜索树。
class BinarySearchTree{
public TreeNode root = null;
class TreeNode{
public int val;
TreeNode left;
TreeNode right;
public TreeNode(int val){
this.val = val;
}
@Override
public String toString() {
return "delNode.val:" + this.val;
}
}
}
2、查找
- 如果root==null,表示树为空,那就查找不到。
- 如果root!=null,那就把我要查找节点的值val和 root的val比较,比root的小,往左子树找;比root的大,往右子树找,如果和root的相等,就返回该节点。
- 如果找到空了还没找到相等的,那就查找不到,返回null。
public TreeNode search(int val){
if (root == null){
return null;
}
TreeNode cur = root;
while (cur != null){
if (val < cur.val) {
cur = cur.left;
} else if (val > cur.val) {
cur = cur.right;
}else {
return cur;
}
}
return null;
}
2、新增
- 如果树的根节点root==null,说明当前树为空,那么就把新增的节点挂在根节点上。
- 如果root!=null,那就把我要新增节点的值val和 root的val比较,比root的小,往左子树找;比root的大,往右子树找,直到找到空,这个空的位置就是我的新增的val该放的位置。
- 那么找到空了,我怎么实现把新增节点放进树里面呢?最简单的方式就是再定义一个标志,每次都标志上一个位置。那么最后找到空时,我用这个标志位的左(右)引用指向新节点即可。
public boolean insert(int val){
if (root == null){
root = new TreeNode(val);
return true;
}
TreeNode cur = root;
TreeNode parent = null;
while (cur != null){
if(val < cur.val){
parent = cur;
cur = cur.left;
}else if (val > cur.val){
parent = cur;
cur = cur.right;
}else {
return false;
}
}
if(val < parent.val){
parent.left = new TreeNode(val);
}else {
parent.right = new TreeNode(val);
}
return true;
}
3、删除
- 删除操作相对于查找和新增来说,难度上升了一个数量级。
@ 分析
cur.left == null
cur 是 root,则 root = cur.right
cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
cur 不是 root,cur 是 parent.right,则 parent.right = cur.right
cur.right == null
cur 是 root,则 root = cur.left
cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
cur 不是 root,cur 是 parent.right,则 parent.right = cur.left
cur.left != null && cur.right != null
需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(右子树最大值),用它的值填补到被删除节点中,再来处理该结点的删除问题。大家可以自己画图试试。
@ 代码
public void remove(int val){
TreeNode cur = root;
TreeNode parent = null;
while (cur != null){
if (val < cur.val) {
parent = cur;
cur = cur.left;
}else if (val > cur.val){
parent = cur;
cur = cur.right;
}else {
removeCur(parent,cur);
return;
}
}
}
private void removeCur(TreeNode parent,TreeNode cur){
if (cur.left == null){
if (cur == root){
root = cur.right;
} else if (cur == parent.left){
parent.left = cur.right;
}else if (cur == parent.right){
parent.right = cur.right;
}
}else if(cur.right == null){
if (cur == root) {
root = cur.left;
}else if (cur == parent.left){
parent.left = cur.left;
} else if (cur == parent.right) {
parent.right = cur.left;
}
}else {
TreeNode target = cur.left;
TreeNode targetParent = cur;
while (target.right != null){
targetParent = target;
target = target.right;
}
cur.val = target.val;
if (target == targetParent.left){
targetParent.left = target.left;
} else if (target == targetParent.right) {
targetParent.right = target.left;
}
}
}
4、如何简单判断代码是否正确?
- 可以在Main类中写一个中序遍历,只要增删查都是有序的,那就证明二叉搜索树相关操作写对了。
public static void inOrderTree(BinarySearchTree.TreeNode root){
if(root == null){
return;
}
inOrderTree(root.left);
System.out.print(root.val + " ");
inOrderTree(root.right);
}