一、二叉排序树概述
1、二叉排序树介绍
(1)简介
- 二叉排序树:BST(Binary Sort(Search) Tree),对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。如果有相同的值,可以将该节点放在左子节点或右子节点。
(2)性质
- 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 它的左右子树也分别为二叉排序树。
二、二叉排序树操作
1、创建二叉排序树
(1)创建一个树结点类
public class TreeNode {
public int value;
public TreeNode left;
public TreeNode right;
public TreeNode(int value) {
this.value = value;
}
@Override
public String toString() {
return "TreeNode{" +
"value=" + value +
'}';
}
/*添加节点的方法*/
public void addNode(TreeNode treeNode){
if(treeNode == null){
return;
}
/*判断传入的节点的值,与当前节点的子树的根节点的值的关系*/
if(treeNode.value < this.value){
/*如果当前节点左子节点为null,直接挂载到当前结点*/
if(this.left == null){
this.left = treeNode;
}else{
/*递归向左子树添加节点*/
this.left.addNode(treeNode);
}
}else{
/*如果当前节点右子节点为null,直接挂载到当前结点*/
if(this.right == null){
this.right = treeNode;
}else{
/*递归向右子树添加节点*/
this.right.addNode(treeNode);
}
}
}
/*中序遍历*/
public void midShow(){
if(this.left != null){
this.left.midShow();
}
System.out.println(this);
if(this.right != null){
this.right.midShow();
}
}
}
(2)创建一个树,用于挂载结点
public class BinarySortTree {
/*根节点*/
private TreeNode rootNode;
/*添加节点*/
public void add(TreeNode treeNode){
if(rootNode == null){
rootNode = treeNode;
} else {
rootNode.addNode(treeNode);
}
}
/*中序遍历*/
public void show(){
if(rootNode != null){
rootNode.midShow();
}else{
System.out.println("该二叉排序树为空");
}
}
}
(3)测试类
public class Test {
public static void main(String[] args) {
int[] arr = {5,0,7,2,9,4,6,1,8,3};
BinarySortTree binarySortTree = new BinarySortTree();
/*循环添加节点到二叉排序树*/
for(int i = 0; i < arr.length; i++){
binarySortTree.add(new TreeNode(arr[i]));
}
/*中序遍历创建的二叉排序树*/
System.out.println("创建完成的二叉排序树为:");
binarySortTree.show();
}
}
2、删除二叉排序树结点
(1)删除叶子结点
删除思路
首先需要找到要删除的结点targetNode;
其次根据targetNode找到其父节点parentNode;
然后找到父节点之后判断targetNode是其父结点左子结点还是右子结点;
最后根据判断情况进行对应删除
(2)删除只有一棵子树的结点
删除思路
首先需要找到要删除的结点targetNode;
其次根据targetNode找到其父节点parentNode;
然后确定targetNode的子结点是左子结点还是右子结点;
然后确定targetNode是父节点的左子结点还是右子结点;
如果targetNode有左子结点并且targetNode是父节点的左子结点,则删除为:parentNode.left = targetNode.left
如果targetNode有左子结点并且targetNode是父节点的右子结点,则删除为:parentNode.left = targetNode.right
如果targetNode有右子结点并且targetNode是父节点的左子结点,则删除为:parentNode.right= targetNode.left
如果targetNode有右子结点并且targetNode是父节点的右子结点,则删除为:parentNode.right= targetNode.right
(3)删除有两棵子树的结点
删除思路
首先需要找到要删除的结点targetNode;
其次根据targetNode找到其父节点parentNode;
然后从targetNode结点的右子树找到最小的结点,用临时变量将最小结点的值保存后删除这个最小结点;
最后将临时变量的值赋给targetNode;
(4)代码示例
创建一个树结点类
public class TreeNode {
public int value;
public TreeNode left;
public TreeNode right;
public TreeNode(int value) {
this.value = value;
}
@Override
public String toString() {
return "TreeNode{" +
"value=" + value +
'}';
}
/*查找要删除的结点*/
public TreeNode searchTarget(int value){
if(value == this.value){
/*查找的值就是当前结点值*/
return this;
} else if (value < this.value){
/*如果查找的值小于当前结点,则向左子树递归查找*/
if (this.left == null) {
return null;
}
return this.left.searchTarget(value);
} else {
/*如果查找的值小于当前结点,则向左子树递归查找*/
if (this.right == null) {
return null;
}
return this.right.searchTarget(value);
}
}
/*查找要删除结点的父节点*/
public TreeNode searchParent(int value){
if ((this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)) {
/*当前节点就是要删除结点的父节点*/
return this;
} else {
if (value < this.value && this.left != null){
/*如果要查找的值小于当前节点的值,并且当前节点的左子结点不为空,向左子树递归*/
return this.left.searchParent(value);
} else if (value > this.value && this.right != null) {
/*如果要查找的值大于当前节点的值,并且当前节点的左子结点不为空,向右子树递归*/
return this.right.searchParent(value);
} else {
return null;
}
}
}
/*添加节点的方法*/
public void addNode(TreeNode treeNode){
if(treeNode == null){
return;
}
/*判断传入的节点的值,与当前节点的子树的根节点的值的关系*/
if(treeNode.value < this.value){
/*如果当前节点左子节点为null,直接挂载到当前结点*/
if(this.left == null){
this.left = treeNode;
}else{
/*递归向左子树添加节点*/
this.left.addNode(treeNode);
}
}else{
/*如果当前节点右子节点为null,直接挂载到当前结点*/
if(this.right == null){
this.right = treeNode;
}else{
/*递归向右子树添加节点*/
this.right.addNode(treeNode);
}
}
}
/*中序遍历*/
public void midShow(){
if(this.left != null){
this.left.midShow();
}
System.out.println(this);
if(this.right != null){
this.right.midShow();
}
}
}
创建一棵树,用于挂载树结点
public class BinarySortTree {
/*根节点*/
private TreeNode rootNode;
/*查找要删除的结点*/
public TreeNode searchTarget(int value){
if(rootNode == null){
return null;
} else {
return rootNode.searchTarget(value);
}
}
/*查找要删除的结点的父节点*/
public TreeNode searchParent(int value){
if(rootNode == null){
return null;
} else {
return rootNode.searchParent(value);
}
}
/*删除结点*/
public void delTreeNode(int value){
if(rootNode == null){
return;
} else {
/*找到要删除的结点targetNode*/
TreeNode targetNode = searchTarget(value);
/*如果没有找到要删除的结点*/
if (targetNode == null) {
return;
}
/*如果该二叉排序树只有一个结点*/
if(rootNode.left == null && rootNode.right == null){
rootNode = null;
return;
}
/*找到要删除结点targetNode的父结点*/
TreeNode parentNode = searchParent(value);
/*如果左右子树都为空,则要删除的结点是叶子结点*/
if (targetNode.left == null && targetNode.right == null) {
//判断要删除结点targetNode是其父结点的左子结点还是右子结点
if (parentNode.left != null && parentNode.left.value == value) {
parentNode.left = null;
} else if (parentNode.right != null && parentNode.right.value == value){
parentNode.right = null;
}
} else if (targetNode.left != null && targetNode.right != null){
/* 如果左右子树有一个不为空,则要删除的结点有一颗子树*/
/* 找到以要删除的结点targetNode为根节点的二叉排序树的右子树中的最小结点的值,
* 用临时变量保存这个最小结点的值,然后删除该最小结点,
* 并将保存的最小结点的值赋给当前根节点targetNode*/
TreeNode tempNode = targetNode.right;
/*循环查找左子结点中的最小值*/
while (tempNode.left != null) {
tempNode = tempNode.left;
}
/*删除这个最小结点*/
delTreeNode(tempNode.value);
int minValue = tempNode.value;
targetNode.value = minValue;
} else {
/*如果左右子树都不为空,则要删除的结点有两颗子树*/
/*如果要删除的结点targetNode有左子结点*/
if (targetNode.left != null) {
if (parentNode != null) {
/*判断删除的结点targetNode是其父节点的左子结点还是右子结点*/
if (parentNode.left.value == value) {
/*如果删除的结点targetNode是其父节点的左子结点*/
parentNode.left = targetNode.left;
} else {
/*如果删除的结点targetNode是其父节点的左子结点*/
parentNode.right = targetNode.left;
}
} else {
rootNode = targetNode.left;
}
/*如果要删除的结点targetNode有右子结点*/
} else {
if (parentNode != null) {
/*判断删除的结点targetNode是其父节点的左子结点还是右子结点*/
if (parentNode.left.value == value){
/*如果删除的结点targetNode是其父节点的左子结点*/
parentNode.left = targetNode.right;
}else{
/*如果删除的结点targetNode是其父节点的左子结点*/
parentNode.right = targetNode.right;
}
}else {
rootNode = targetNode.right;
}
}
}
}
}
/*添加节点*/
public void add(TreeNode treeNode){
if(rootNode == null){
rootNode = treeNode;
} else {
rootNode.addNode(treeNode);
}
}
/*中序遍历*/
public void show(){
if(rootNode != null){
rootNode.midShow();
}else{
System.out.println("该二叉排序树为空");
}
}
}
测试1:删除叶子节点
public class Test {
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
BinarySortTree binarySortTree = new BinarySortTree();
/*循环添加节点到二叉排序树*/
for(int i = 0; i < arr.length; i++){
binarySortTree.add(new TreeNode(arr[i]));
}
/*中序遍历创建的二叉排序树*/
System.out.println("创建完成的二叉排序树为:");
binarySortTree.show();
/*测试删除叶子节点*/
binarySortTree.delTreeNode(2);
binarySortTree.delTreeNode(5);
binarySortTree.delTreeNode(9);
System.out.println("\n删除叶子结点后的二叉排序树为:");
binarySortTree.show();
}
}
输出结果为:
创建完成的二叉排序树为:
TreeNode{value=1}
TreeNode{value=2}
TreeNode{value=3}
TreeNode{value=5}
TreeNode{value=7}
TreeNode{value=9}
TreeNode{value=10}
TreeNode{value=12}
删除叶子结点后的二叉排序树为:
TreeNode{value=1}
TreeNode{value=3}
TreeNode{value=7}
TreeNode{value=10}
TreeNode{value=12}
测试2:删除当前结点只有一个结点的结点
public class Test {
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
BinarySortTree binarySortTree = new BinarySortTree();
/*循环添加节点到二叉排序树*/
for(int i = 0; i < arr.length; i++){
binarySortTree.add(new TreeNode(arr[i]));
}
/*中序遍历创建的二叉排序树*/
System.out.println("创建完成的二叉排序树为:");
binarySortTree.show();
/*测试删除当前结点只有一个结点的结点*/
binarySortTree.delTreeNode(1);
System.out.println("\n删除有一个子结点的结点后的二叉排序树为:");
binarySortTree.show();
}
}
输出结果为:
创建完成的二叉排序树为:
TreeNode{value=1}
TreeNode{value=2}
TreeNode{value=3}
TreeNode{value=5}
TreeNode{value=7}
TreeNode{value=9}
TreeNode{value=10}
TreeNode{value=12}
删除有一个子结点的结点后的二叉排序树为:
TreeNode{value=2}
TreeNode{value=3}
TreeNode{value=5}
TreeNode{value=7}
TreeNode{value=9}
TreeNode{value=10}
TreeNode{value=12}
测试3:删除当前结点有两个结点的结点
public class Test {
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
BinarySortTree binarySortTree = new BinarySortTree();
/*循环添加节点到二叉排序树*/
for(int i = 0; i < arr.length; i++){
binarySortTree.add(new TreeNode(arr[i]));
}
/*中序遍历创建的二叉排序树*/
System.out.println("创建完成的二叉排序树为:");
binarySortTree.show();
/*测试删除当前结点有两个结点的结点*/
binarySortTree.delTreeNode(3);
binarySortTree.delTreeNode(10);
binarySortTree.delTreeNode(7);
System.out.println("\n删除有两个子结点的结点后的二叉排序树为:");
binarySortTree.show();
}
}
输出结果为:
创建完成的二叉排序树为:
TreeNode{value=1}
TreeNode{value=2}
TreeNode{value=3}
TreeNode{value=5}
TreeNode{value=7}
TreeNode{value=9}
TreeNode{value=10}
TreeNode{value=12}
删除有两个子结点的结点后的二叉排序树为:
TreeNode{value=1}
TreeNode{value=2}
TreeNode{value=5}
TreeNode{value=9}
TreeNode{value=12}