11.4二叉排序树
二叉排序树的介绍:二叉排序树:BST: (Binary Sort(Search) Tree), 对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当 前节点的值小,右子节点的值比当前节点的值大。特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点
二叉排序树的构建与遍历较为简单直接在代码中实现
二叉排序树的删除节点的思路,删除情况比较复杂,有下面三种情况需要考虑:
1、 删除叶子节点 (比如:2, 5, 9, 12)
(1) 需求先去找到要删除的结点 targetNode
(2) 找到 targetNode 的 父结点 parent(只有找到父节点才能删除当前节点)
(3) 确定 targetNode是parent的左子结点还是右子结点
(4) 根据前面的情况来对应删除:
删除左子结点 parent.left = null
删除右子结点 parent.right = nul
2、 删除只有一颗子树的节点 (比如:1)
(1) 需求先去找到要删除的结点targetNode
(2) 找到targetNode的父结点parent
(3) 确定 targetNode 的子结点是左子结点还是右子结点
(4) 确定targetNode 是 parent 的左子结点还是右子结点。在3、4结合之下有四种情况,分别是:
1、target是parent是左子节点,target的子节点是左子节点
删除target,parent.left = target.left
2、target是parent是左子节点,target的子节点是右子节点
删除target,parent.left = target.right
3、target是parent是右子节点,target的子节点是左子节点
删除target,parent.right = target.left
4、target是parent是右子节点,target的子节点是右子节点
删除target,parent.right = target.right
3、 删除有两颗子树的节点. (比如:7, 3,10 )
(1) 需求先去找到要删除的结点 targetNode
(2) 找到 targetNode 的父结点 parent
(3) 从 targetNode 的右子树找到最小的结点,将其放在要删除的结点的位置之上。或者也可以这样做,从targetNode的左子树找到最大的结点,将其放在要删除的结点之上
package com.atguigu11.binarySortTree;
/**
* @author peng
* @date 2021/12/1 - 16:22
* <p>
* 实现二叉排序树
*/
public class BinarySortTreeDemo {
public static void main(String[] args) {
int[] array = {7, 3, 10, 12, 5, 1, 9,2};
BinarySortTree binarySortTree = new BinarySortTree();
System.out.println("测试二叉排序树:");
for (int i = 0; i < array.length; i++) {
binarySortTree.binarySortTreeAdd(new Node(array[i]));
}
System.out.println("遍历二叉排序树:");
binarySortTree.binarySortTreeInfixOrder();
System.out.println("删除叶子节点:");
binarySortTree.deleteNode(7);
System.out.println("删除叶子节点之后,排序二叉树为:");
binarySortTree.binarySortTreeInfixOrder();
}
}
/**
* 创建二叉排序树
*/
class BinarySortTree {
private Node root;//根节点
/**
* 添加节点的方法
*/
public void binarySortTreeAdd(Node node) {
if (root == null) {
//如果当前树为空
root = node;
} else {
root.add(node);
}
}
/**
* 中序遍历二叉排序树
*/
public void binarySortTreeInfixOrder() {
if (root == null) {
System.out.println("当前二叉排序树为空,不能进行中序遍历!");
return;
}
root.infixOrder();
}
/**
* 查找要删除的节点
*/
public Node binarySortTreeSearchNode(int data) {
if (root == null) {
return null;
} else {
return root.search(data);
}
}
/**
* 查找要删除节点的父节点
*/
public Node binarySortTreeSearchParent(int data) {
if (root == null) {
return null;
} else {
return root.searchParent(data);
}
}
/**
* 删除节点
*/
public void deleteNode(int data) {
if (root == null) {
System.out.println("当前二叉排序树为空,不能进行删除操作!");
return;
} else {
//先看看能不能找到要删除的那个节点
Node targrtNode = binarySortTreeSearchNode(data);
if (targrtNode == null) {
//如果没有找到要删除的那一个节点,那么直接退出
return;
}
//如果要删除的根节点,且只有一个根节点(也就是说根节点的左右节点都为空)
if (root.left == null && root.right == null) {
//此时直接将根节点置为空
root = null;
return;
}
//寻找targetNode的父节点
Node parent = binarySortTreeSearchParent(data);
//如果要删除的节点是叶子节点
if (targrtNode.left == null && targrtNode.right == null) {
//再进行判断,如果要删除的节点是找到的节点的左子结点
if (parent.left != null && parent.left.data == data) {
//将需要删除的节点置空
parent.left = null;
} else if (parent.right != null && parent.right.data == data) {
//如果要删除的节点是找到的父节点的右子节点,那么将找到的父节点的右子节点置空
parent.right = null;
}
} else if (targrtNode.left != null && targrtNode.right != null) {
//如果要删除的节点有左右两个节点
//找到右子树中最小的那一个节点,将其放在target的位置之上
//还有一种解决思路,那就是在左子树中找到最大的那一个节点,将其放在target的位置之上
int minData = delRightTreeMin(targrtNode.right);//这个方法在下面,主要的功能是找到二叉树中最小的那个节点删除并返回
targrtNode.data = minData;
} else {
//剩下的最后一种情况就是删除只有一个孩子节点(也就是只有一个节点:左子节点或者是右子节点)的节点
//如果要删除的节点有左子节点,且要删除的那个节点是其父节点左孩子节点
if (targrtNode.left != null) {
if (parent != null) {
//判断parent不能为空,是因为如果此时只有一个根节点和一个左孩子节点且要删除的节点是根节点,因为该根节点没有父节点
//所以会发生空指针异常
if (parent.left.data == targrtNode.data) {
//要删除的那个节点是其父节点左孩子节点
parent.left = targrtNode.left;
} else {
//要删除的那个节点是其父节点右孩子节点
parent.right = targrtNode.left;
}
} else {
//如果此时只有一个根节点和一个左子节点,且要删除的节点是根节点,那么直接让root指向根节点的左孩子节点即可
root = targrtNode.left;
}
} else {
if (parent != null) {
//这里判断parent不能为空的原因和上面的一样
// 因为如果此时只有一个根节点和一个右孩子节点且要删除的节点是根节点,因为该根节点没有父节点,所以会发生空指针异常
//如果要删除的那个节点有右孩子节点
if (parent.left.data == targrtNode.data) {
//如果要删除的节点是其父节点的左孩子节点
parent.left = targrtNode.right;
} else {
//如果要删除的那个节点是其父节点的右孩子节点
parent.right = targrtNode.right;
}
} else {
root = targrtNode.right;
}
}
}
}
}
/**
* 实现
* 1、返以node节点为根节点的最小节点的值
* 2、删除以node为根节点的二叉排序树的最小节点的值
*
*/
public int delRightTreeMin (Node node){
Node target = node;
//循环地向左寻找,直到找到最小的那一个节点
while (target.left != null) {
target = target.left;
}
//退出循环之后,target指向该二叉树最小的那一个节点
//删除最小的那一个节点
deleteNode(target.data);
//返回最小的那一个节点的值
return target.data;
}
}
/**
* 创建叶子节点
*/
class Node {
public int data;//节点的值
public Node left;//左孩子节点
public Node right;//右孩子节点
public Node(int data) {
this.data = data;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
'}';
}
/**
* 利用递归实现添加叶子节点
*/
public void add(Node node) {
if (node == null) {
return;
}
if (node.data < this.data) {
//如果插入节点的值小于当前比较节点的值,向左寻找
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 infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}
/**
* 查找要删除的节点
*/
public Node search(int data) {
if (data == this.data) {
//如果要删除的节点是当前节点
return this;
} else if (data < this.data) {
//如果要删除的节点小于当前的节点,应该向左子树寻找
if (this.left == null) {
//如果左子节点为空,说明没找到
return null;
}
return this.left.search(data);
} else {
if (this.right == null) {
//如果右子树为空,说明没找到
return null;
}
return this.right.search(data);//否则继续向右寻找
}
}
/**
* 查找要删除节点的父节点
* data :要删除的节点的值
* 返回要删除的节点的父节点,如果没有就返回null
*/
public Node searchParent(int data) {
if ((this.left != null && this.left.data == data) || (this.right != null && this.right.data == data)) {
//如果要删除的节点是当前节点的左节点或者是右节点,那么直接返回当前的节点
return this;
} else {
//如果要删除的节点小于当前的节点,且当前节点的左子节点不为空,那么就向左递归查找
if (data < this.data && this.left != null) {
return this.left.searchParent(data);
} else if (data >= this.data && this.right != null) {
//如果要删除的节点大于当前的节点,且当前节点的右子节点不为空
return this.right.searchParent(data);
} else {
return null;
}
}
}
}