二叉排序树
基本介绍
二叉排序树:BST(BinarySort(Search)Tree),对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。
特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点,尽量避免出现相同值
给定数组{5,3,1,4,6,9},对应的二叉排序树如下所示:
二叉排序树创建和遍历以及删除节点
因为二叉树的创建与遍历比较简单,这里不做分析(注意,采用中序遍历二叉树结果才是按顺序输出)。着重分析二叉树的删除。
二叉排序树删除节点的思路分析
二叉排序树的删除分为三种情况,下面逐一分析;
情况一:删除叶子节点,比如上图中的1,4,9节点;
- 需要先去找到要删除的结点 target
- 再找到 target 的父结点 parent
- 确定 target 是 parent 的左子结点还是右子结点
- 如果是左子节点:parent.left=null;
如果是右子节点:parent.right=null。
情况二:删除只有一个子节点的节点,比如上图中的6。
- 需要先去找到要删除的结点 target
- 再找到 target 的父结点 parent
- 确定 target 是 parent 的左子结点还是右子结点
- 确定 target 的子结点是左子结点还是右子结点
- 如果 target 是 parent 的左子节点;
(1)如果 target 的左子节点有值,parent.left = targetNode.left;
(2)如果 target 的右子节点有值,parent.left = targetNode.right; - 如果 target 是 parent 的右子节点;
(1)如果 target 的左子节点有值,parent.right = targetNode.left;
(2)如果 target 的右子节点有值,parent.right = targetNode.right;
情况三:删除有两个子节点的节点,比如上图中的3,5。
- 需要先去找到要删除的结点 target
- 从 target 的右子树找到最小的结点
- 用一个临时变量 temp,将最小结点的值保存
- 删除该最小结点
- target.value=temp
代码实现
首先编写一个封装节点的类。
/**
* 创建节点类,封装数据
*/
class Node {
int value;//封装数据
Node left;
Node right;
public Node(int value) {
this.value = value;
}
/**
* 添加节点
*
* @param node 待添加的节点
*/
public void add(Node node) {
if (node == null) {
return;
}
if (node.value < this.value) {
//代码执行到这里说明待添加节点的值小于当前节点的值,
//应当添加到当前节点的左边
if (this.left == null) {
//如果当前节点的左子节点为空,直接添加
this.left = node;
} else {
//否则向左递归添加
this.left.add(node);
}
} else {
//这里的原理同上面的一样。
if (this.right == null) {
this.right = node;
} else {
this.right.add(node);
}
}
}
/**
* 中序遍历
*/
public void inorder() {
//向左递归
if (this.left != null) {
this.left.inorder();
}
System.out.println(this);
//向右递归
if (this.right != null) {
this.right.inorder();
}
}
/**
* 查找目标节点
*
* @param value 待查询的值
* @return Node 返回查询的结果,若是查询不到,返回null
*/
public Node findTarget(int value) {
if (value == this.value) {
return this;
}
if (value < this.value) {
if (this.left != null) {
return this.left.findTarget(value);
} else {
return null;
}
} else {
if (this.right != null) {
return this.right.findTarget(value);
} else {
return null;
}
}
}
/**
* @param value 待查询的值
* @return 返回要查询的值的父节点,若是查询不到,返回null
*/
public Node findParent(int value) {
//如果当前节点就是要查询节点的父节点,返回当前节点
if ((this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)) {
return this;
}
if (this.left != null && value < this.value) {
return this.left.findParent(value);
} else if (this.right != null && value >= this.value) {
return this.right.findParent(value);
} else {
return null;
}
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}
然后编写管理二叉排序树的类
/**
* 管理二叉排序树的类
*/
class BinarySortTree {
private Node root;
/**
* 添加的方法
*
* @param node 待添加的节点
*/
public void add(Node node) {
if (root == null) {
root = node;
} else {
root.add(node);
}
}
public void delNode(int value) {
if (root == null) {
System.out.println("二叉排序树为空,无法删除");
return;
}
//查询目标节点
Node target = root.findTarget(value);
if (target == null) {
System.out.println("未找到要删除的节点");
return;
}
//如果二叉树中只有根节点,并且目标节点不为空,说明目标节点就是根节点。
//所以直接将根节点置空
if (root.right == null && root.left == null) {
root = null;
return;
}
//查询目标节点的父节点
Node parent = root.findParent(value);
if (target.left == null && target.right == null) {
//情况一:说明待删除节点是叶子节点
if (parent.left == target) {
//说明待删除节点是父节点的左子节点
parent.left = null;
} else {
//说明待删除节点是父节点的右子节点
parent.right = null;
}
} else if (target.left != null && target.right != null) {
//情况二:说明待删除节点拥有两个子节点
//处理思路一:向目标节点的右边查询最小的节点,将值赋给目标节点,然后将右子树最小节点删除
target.value = findRightMinVal(target.right);
//处理思路二:向目标节点的左边查询最大的节点,将值赋给目标节点,然后将左子树最大节点删除
//注:这里只展示思路一的处理方法,有兴趣可以自己完成思路二
} else {
if (parent != null) {
//情况三:说明待删除节点拥有一个子结点
if (parent.left == target) {
//1.说明目标节点是父节点的左子节点
if (target.left != null) {
//(1)说明目标节点的左子节点有值
parent.left = target.left;
} else {
//(2)说明目标节点的右子节点有值
parent.left = target.right;
}
} else {
//2.说明目标节点是父节点的右子节点
if (target.left != null) {
//(1)说明目标节点的左子节点有值
parent.right = target.left;
} else {
//(2)说明目标节点的右子节点有值
parent.right = target.right;
}
}
} else {
//此时说明待删除节点为根节点,并且只有左子节点或者右子节点中的一个有值
if (target.left != null) {
root = target.left;
} else {
root = target.right;
}
}
}
}
/**
* 该方法有两个任务:
* 1.查询值最小的节点,将最小值返回
* 2.将值最小节点删除
*
* @param node 从该节点向左查询
* @return 返回最小值
*/
private int findRightMinVal(Node node) {
while (node.left != null) {
node = node.left;
}
//经过循环处理后,node就是代表值最小的节点
int minVal = node.value;
delNode(minVal);//完成任务2
return minVal;//完成任务1
}
/**
* 前序遍历
*/
public void inorder() {
if (root == null) {
System.out.println("二叉排序树为空不能遍历");
return;
}
root.inorder();
}
}
最后在主方法中测试代码是否正确
package com.atschool.binarysorttree;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/14 19:31
*/
public class BinarySortTreeDemo {
public static void main(String[] args) {
//测试添加方法
int[] arr = {5, 3, 1, 4, 6, 9};
BinarySortTree binarySortTree = new BinarySortTree();
for (int i : arr) {
binarySortTree.add(new Node(i));
}
System.out.println("原来的二叉树");
binarySortTree.inorder();
System.out.println("-------------------");
//测试删除方法
//9为叶子节点
binarySortTree.delNode(9);
System.out.println("删除9叶子节点后的二叉树");
binarySortTree.inorder();
System.out.println("-------------------");
//删除9后,8为拥有一个节点的非叶子节点
binarySortTree.delNode(8);
System.out.println("删除8非叶子节点后的二叉树");
binarySortTree.inorder();
System.out.println("-------------------");
//3为一个拥有两个节点的非叶子节点
binarySortTree.delNode(3);
System.out.println("删除3非叶子节点后的二叉树");
binarySortTree.inorder();
}
}
测试结果
结语
只要能收获甜蜜,荆棘丛中也有蜜蜂忙碌的身影,未来的你一定会感谢现在努力的自己。