一、二叉排序树的定义
二叉排序树(也称二叉查找树)或者是一棵空树,或者是具有下列特性的二叉树:
1)若左子树非空,则左子树上所有结点的值均小于根结点的值。
2)若右子树非空,则右子树上所有结点的值均大于根结点的值。
3)左、右子树也分别是一棵二叉排序树。根据二叉排序树的定义,左子树结点值
<
根结点值<
右子树结点值,所以对二叉排序树进行中序遍历,可以得到一个递增的有序序列。
二、二叉排序树的查找
二叉排序树的查找是从根结点开始,沿某个分支逐层向下比较的过程。若二叉排序树非空,先将给定值与根结点的关键字比较,若相等,则查找成功;若不等,如果小于根结点的关键字,则在根结点的左子树上查找,否则在根结点的右子树上查找。这显然是一个递归的过程。
三、二叉排序树的插入
二叉排序树作为一种动态树表,其特点是树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字值等于给定值的结点时再进行插入的。
插入结点的过程如下:若原二叉排序树为空,则直接插入结点;否则,若关键字k小于根结点值,则插入到左子树,若关键字k大于根结点值,则插入到右子树。插入的结点一定是一个新添加的叶结点,且是查找失败时的查找路径上访问的最后一个结点的左孩子或右孩子。
四、二叉排序树的删除
在二叉排序树中删除一个结点时,不能把以该结点为根的子树上的结点都删除,必须先把被删除结点从存储二叉排序树的链表上摘下,将因删除结点而断开的二叉链表重新链接起来,同时确保二叉排序树的性质不会丢失。删除操作的实现过程按3种情况来处理:
1)若被删除结点z是叶结点,则直接删除,不会破坏二叉排序树的性质。
2)若结点z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,替代z的位置。
3)若结点z有左、右两棵子树,则令z的直接后继(或直接前驱)替代z,然后从二叉排序树中删去这个直接后继(或直接前驱),这样就转换成了第一或第二种情况。
五、二叉排序树的代码实现
package com.haiyang.datastructure.binarysorttree;
import java.util.ArrayList;
import java.util.List;
/**
* @author haiYang
* @create 2022-01-22 9:41
*/
public class BinarySortTree {
private static Node tRoot = null;//根结点
public static void main(String[] args) {
int[] ints = {2, 16, 18, 9, 34, 8, 7, 1};
//创建一颗二叉排序树
for (int i = 0; i < ints.length; i++) {
Node node = new Node(ints[i]);
addNode(tRoot, node);
}
//删除目标结点
deleteNode(16);
//删除后的树
preOrder(tRoot);
}
/**
* 先序遍历 用于查看删除后结果
*
* @param root
*/
private static void preOrder(Node root) {
if (root == null) {
return;
}
System.out.println(root);
if (root.getLeft() != null) {
preOrder(root.getLeft());
}
if (root.getRight() != null) {
preOrder(root.getRight());
}
}
/**
* 删除目标结点
*
* @param targetNode 要删除结点的value
*/
private static void deleteNode(int targetNode) {
//查找目标结点的父结点和目标结点[parent,target]
List<Node> nodes = searchNode(tRoot, targetNode);
if (nodes == null) {//未找到
System.out.println("目标结点不存在");
return;
}
Node parent = nodes.get(0);//获取目标结点的父结点
Node target = nodes.get(1);//获取目标结点
//当目标结点的左右孩子都为null时
if (target.getLeft() == null && target.getRight() == null) {
if (parent == null) {//如果是根结点,删除后树为空
tRoot = null;
} else if (target == parent.getLeft()) {//如果删除节点为父结点的左孩子,则将父结点parent的左孩子置为null;
parent.setLeft(null);
} else {如果删除节点为父结点的右孩子,则将父结点parent的右孩子置为null;
parent.setRight(null);
}
}
//当目标结点的左孩子为null,右孩子非空时
if (target.getLeft() == null && target.getRight() != null) {
if (parent == null) {//如果是根结点,删除后树为右孩子
tRoot = target.getRight();
} else if (target == parent.getLeft()) {
parent.setLeft(target.getRight());
} else {
parent.setRight(target.getRight());
}
}
//当目标结点的右孩子为null,左孩子非空时
if (target.getLeft() != null && target.getRight() == null) {
if (parent == null) {//如果是根结点,删除后树为左孩子
tRoot = target.getLeft();
} else if (target == parent.getLeft()) {
parent.setLeft(target.getLeft());
} else {
parent.setRight(target.getLeft());
}
}
//左右孩子都非空时
if (target.getLeft() != null && target.getRight() != null) {
//寻找中序遍历的第一个孩子,也就是右子树的最小值
Node node = infixOrder(target.getRight());
Node r = node;//临时变量
deleteNode(node.getValue());//转为删除中序遍历的第一个孩子
target.setValue(r.getValue());//将第一个孩子的值赋给目标结点
}
}
/**
* @param root 根结点
* @param targetNode 目标结点
* @return 目标结点的父结点和目标结点[parent, target]
*/
private static List<Node> searchNode(Node root, int targetNode) {
List<Node> nodes = new ArrayList<>();
Node pre = null;//标记父亲结点
while (true) {
if (targetNode == root.getValue()) {
nodes.add(pre);//当根结点就是要删除结点,则parent为null
nodes.add(root);
return nodes;
}
if (targetNode < root.getValue()) {//当目标结点小于当前结点,向左查找
if (root.getLeft() == null) {//未找到,返回null
return null;
} else if (root.getLeft().getValue() == targetNode) {//找到返回
pre = root;
nodes.add(pre);
nodes.add(root.getLeft());
return nodes;
} else {
pre = root;
root = root.getLeft();
}
}
if (targetNode > root.getValue()) {//当目标结点大于当前结点,向右查找
if (root.getRight() == null) {
return null;
} else if (root.getRight().getValue() == targetNode) {
pre = root;
nodes.add(pre);
nodes.add(root.getRight());
return nodes;
} else {
pre = root;
root = root.getRight();
}
}
}
}
/**
* @param root
* @return 中序遍历的第一个结点
*/
private static Node infixOrder(Node root) {
Node p = root;
while (p.getLeft() != null) {
p = p.getLeft();
}
return p;
}
/**
* 添加结点
*
* @param root 根结点
* @param node 待添加结点
*/
private static void addNode(Node root, Node node) {
if (node == null) {
return;
} else if (root == null) {//如果是第一个结点,初始化为根结点
tRoot = node;
} else {
if (node.getValue() < root.getValue()) {
if (root.getLeft() == null) {
//添加到当前位置
root.setLeft(node);
} else {
//如果比当前结点小,且左孩子不为空,递归调用
addNode(root.getLeft(), node);
}
} else {
if (root.getRight() == null) {
//添加到当前位置
root.setRight(node);
} else {
//如果比当前结点大,且右孩子不为空,递归调用
addNode(root.getRight(), node);
}
}
}
}
}
//结点类
class Node {
private int value;//当前结点值
private Node left;//左孩子
private Node right;//右孩子
public Node() {
}
public Node(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int 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 +
'}';
}
}