二叉排序树的概述
二叉排序树:BST(Binary Sort(Search) Tree), 对于二叉排序树的任何一个非叶子节点,
要求当前节点值大于其左子节点值,当前节点值小于其右子节点值
特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点
代码所对应的 数组 与 二叉排序树 如下
数组{7, 3, 10, 12, 5, 1, 9, 2}
数组对应的二叉排序树
package com.zzb.tree;
import java.io.Serializable;
/**
* @Auther: Administrator
* @Date: 2019/10/14 00:07
* @Description:
* 二叉排序树:BST(Binary Sort(Search) Tree), 对于二叉排序树的任何一个非叶子节点,
* 要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大
* 特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点
*/
public class BinaryTreeDemo03 {
public static void main(String[] args) {
// 添加元素
testAdd();
// 删除节点
testDelOne();
}
/**
* 测试添加元素节点
* 根据数组创建成对应的二叉排序树,并使用中序遍历二叉排序树,比如:数组为{7, 3, 10, 12, 5, 1, 9, 2}
*/
private static void testAdd() {
// 创建二叉排序树
BinarySortTree binarySortTree = new BinarySortTree();
// 数组
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
for (int i = 0; i < arr.length; i++) {
// 添加元素节点
binarySortTree.add(new Node(arr[i]));
}
// 中序遍历
System.out.println("----------------中序遍历----------------");
binarySortTree.infixOrder();
/*----------------中序遍历----------------
Node{value=1}
Node{value=2}
Node{value=3}
Node{value=5}
Node{value=7}
Node{value=9}
Node{value=10}
Node{value=12}*/
}
// 测试删除节点
private static void testDelOne() {
// 创建二叉排序树
BinarySortTree binarySortTree = new BinarySortTree();
// 数组
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
for (int i = 0; i < arr.length; i++) {
// 添加元素节点
binarySortTree.add(new Node(arr[i]));
}
System.out.println("----------------删除节点前的中序遍历----------------");
binarySortTree.infixOrder();
// 删除节点
binarySortTree.delOne(9);
System.out.println("----------------删除节点后的中序遍历----------------");
binarySortTree.infixOrder();
}
}
// 二叉排序树
class BinarySortTree {
private Node root;
// 二叉排序树添加元素节点
public void add(Node node) {
// 根节点非空判断
if(this.getRoot() == null) { // null
this.setRoot(node);
} else { // not null
this.getRoot().add(node);
}
}
// 二叉排序树中序遍历
public void infixOrder() {
if(this.getRoot() == null) {
System.out.println("二叉树为空,无法遍历!");
} else {
this.getRoot().infixOrder();
}
}
// 二叉排序树删除节点
/*有三种情况需要考虑
(1)删除叶子节点 (比如:2, 5, 9, 12)
(2)删除只有一颗子树的节点 (比如:1)
(3)删除有两颗子树的节点(比如:7, 3, 10 )*/
/*
第一种情况:
删除叶子节点 (比如:2, 5, 9, 12)
思路
(1) 需求先去找到要删除的结点 targetNode
(2) 找到 targetNode 的 父结点 parent
(3) 确定 targetNode 是 parent 的左子结点 还是右子结点
(4) 根据前面的情况来对应删除
左子结点 parent.left = null
右子结点 parent.right = null
第二种情况: 删除只有一颗子树的节点 (比如:1)
思路
(1) 需求先去找到要删除的结点 targetNode
(2) 找到 targetNode 的 父结点 parent
(3) 确定 targetNode 的子结点是左子结点还是右子结点
(4) targetNode 是 parent 的左子结点还是右子结点
(5) 如果 targetNode 有左子结点
5.1) 如果 targetNode 是 parent 的左子结点
parent.left = targetNode.left
5.2) 如果 targetNode 是 parent 的右子结点
parent.right = targetNode.left
(6) 如果 targetNode 有右子结点
6.1) 如果 targetNode 是 parent 的左子结点
parent.left = targetNode.right
6.2) 如果 targetNode 是 parent 的右子结点
parent.right = targetNode.right
情况三 : 删除有两颗子树的节点. (比如:7, 3, 10 )
思路
(1) 需求先去找到要删除的结点 targetNode
(2) 找到 targetNode 的 父结点 parent
(3) 从 targetNode 的右子树找到最小的结点
(4) 用一个临时变量,将 最小结点的值保存 temp
(5) 删除该最小结点
(6) targetNode.value = temp*/
public void delOne(int value) {
// 根节点非空判断
if(this.getRoot() == null) {
return;
} else {
// (1) 需求先去找到要删除的结点 targetNode
Node targetNode = search(value);
// 没有找到要删除的节点
if(targetNode == null) {
return;
}
// 走到该位置,并且二叉排序树只有一个跟节点
if(this.getRoot() != null
&& this.getRoot().getLeft() == null
&& this.getRoot().getRight() == null) {
this.setRoot(null);
return;
}
// (2) 找到 targetNode 的 父结点 parent
Node parentNode = searchParent(value);
// 情况一:要删除的节点时叶子节点
if(targetNode.getLeft() == null && targetNode.getRight() == null) {
// 判断是左叶子节点、还是有叶子节点
if(parentNode.getLeft() != null && parentNode.getLeft().getValue() == value) { // 左子节点
parentNode.setLeft(null);
} else if(parentNode.getRight() != null && parentNode.getRight().getValue() == value) { // 右子节点
parentNode.setRight(null);
}
// 情况三:要删除的节点存在左、右子树
} else if(targetNode.getLeft() != null && targetNode.getRight() != null) {
// 从 targetNode 的右子树找到最小的结点
Integer minVal = delRightTreeMin(targetNode.getRight());
targetNode.setValue(minVal);
} else { // 情况二:要删除的节点存在一棵子树
if(targetNode.getLeft() != null) { // targetNode有左子节点
if(parentNode != null) {
if(parentNode.getLeft().getValue() == value) { // targetNode是parentNode的左子节点
parentNode.setLeft(targetNode.getLeft());
} else { // targetNode是parentNode的右子节点
parentNode.setRight(targetNode.getLeft());
}
} else {
this.setRoot(targetNode.getLeft()); // 删除的是根节点,则根节点就不会存在父节点这种情况
}
} else { // targetNode有右子节点
if(parentNode != null) {
if(parentNode.getLeft().getValue() == value) { // targetNode是parentNode的左子节点
parentNode.setLeft(targetNode.getRight());
} else { // targetNode是parentNode的右子节点
parentNode.setRight(targetNode.getRight());
}
} else {
this.setRoot(targetNode.getRight()); // 删除的是根节点,则根节点就不会存在父节点这种情况
}
}
}
}
}
// 找到要删除的结点 targetNode
private Node search(Integer value) {
if(this.getRoot() == null) {
return null;
} else {
return this.getRoot().search(value);
}
}
// 找到 targetNode 的 父结点 parentNode
private Node searchParent(Integer value) {
if(this.getRoot() == null) {
return null;
} else {
return this.getRoot().searchParent(value);
}
}
/**
* 从 targetNode 的右子树找到最小的结点
* @param node 传入的结点(当做二叉排序树的根结点)
* @return 返回的 以node为根结点的二叉排序树的最小结点的值
*/
private Integer delRightTreeMin(Node node) {
Node target = node;
// 循环遍历左子节点,就会找到最小值的节点
while (target.getLeft() != null) {
target = target.getLeft();
}
// 删除最小节点,就是删除叶子节点的情况
delOne(target.getValue());
return target.getValue();
}
public Node getRoot() {
return root;
}
public void setRoot(Node root) {
this.root = root;
}
}
// 元素节点类
class Node implements Serializable {
private static final long serialVersionUID = 5554155106585225764L;
private Integer value;
private Node left;
private Node right;
// 添加元素节点
public void add(Node node) {
// 待添加节点的非空判断
if(node == null) {
return;
}
if(this.getValue() > node.getValue()) { // 当前节点大于添加节点
if(this.getLeft() == null) { // 添加到当前节点的左边
this.setLeft(node);
} else { // 递归判断
this.getLeft().add(node);
}
} else { // 当前节点小于添加节点
if(this.getRight() == null) { // 添加到当前节点的右边
this.setRight(node);
} else { // 递归调用
this.getRight().add(node);
}
}
}
// 查找要删除的节点
public Node search(Integer value) {
if(this.getValue() == value) { // 当前节点等于查找节点
return this;
} else if(this.getValue() > value) { // 当前节点大于查找节点
if(this.getLeft() == null) {
return null;
} else { // 左子树找
return this.getLeft().search(value);
}
} else { // 当前节点小于查找节点
if(this.getRight() == null) {
return null;
} else { // 右子树找
return this.getRight().search(value);
}
}
}
// 查找要删除节点的父节点
public Node searchParent(Integer value) {
if((this.getLeft() != null && this.getLeft().getValue() == value)
|| (this.getRight() != null && this.getRight().getValue() == value)) {
return this;
} else {
if(this.getValue() > value && this.getLeft() != null) { // 左子树找
return this.getLeft().searchParent(value);
} else if(this.getValue() < value && this.getRight() != null) { // 右子树找
return this.getRight().searchParent(value);
} else { // 没找到父节点
return null;
}
}
}
/**
* 中序遍历
* 每个节点都会执行中序遍历方法中的三个动作,涉及到递归方法的压栈与弹栈
*/
public void infixOrder() {
// (1)判断如果当前节点的左子节点不为空,则当前节点的左子节点递归执行中序遍历方法进行中序遍历
if(this.getLeft() != null) {
this.getLeft().infixOrder();
}
// (2)输出当前节点(初始节点是根节点root节点)
System.out.println(this);
// (3)判断如果当前节点的右子节点不为空,则当前节点的右子节点递归执行中序遍历方法进行中序遍历
if(this.getRight() != null) {
this.getRight().infixOrder();
}
}
public Node(Integer value) {
this.value = value;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}