在本篇文中对于二叉排序树的定义:
- 若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值
- 若右子树不空,则右子树上所有结点的值均大于它的根结点的值
- 左、右子树也分别为二叉排序树
本文创建的类以及该类中的元素:
类名 | 元素 |
BST_Demo(主类) | |
BinarySortTree | BST_Node root |
BST_Node | int value |
BST_Node left | |
BST_Node right |
二叉排序树的创建:
二叉排序树的创建思路如下:
1.在BST_Node类中定义一个添加结点的方法AddNode(BST_Node node)
2.在BinarySortTree类中定义新的方法TreeAddNode方法并嵌套调用AddNode方法,添加判断条件。使得主函数中可以通过BinarySortTree对象添加元素。
3.在主函数中利用for循环遍历参与二叉排序树构建的数组,并在每次循环中都调用TreeAddNode方法,传参为遍历的数组元素
后文为具体思路以及代码实现!
BST_Node类:public void AddNode(BST_Node node)的定义
递归的思想实现。定义一个指针指向当前节点(之后的描述中当前节点指target),此时存在四种情况:
1.左子结点为空,待插入结点的值小于等于当前结点的值 将当前节点的左子结点设为node
2.右子结点为空,且待插入结点的值大于当前结点的值 将当前节点的右子结点设为node
注:以上两个种情况是递归结束的条件。
3.左子结点不为空,且待插入结点的值小于等于当前结点的值 向当前结点的左子结点进行递归
4.右子结点不为空,且待插入结点的值大于当前结点的值 向当前结点的右子节点进行递归
public void AddNode(BST_Node node){
if(this.left==null && node.value<=this.value){
this.left=node;
}else if(this.right==null && node.value>this.value){
this.right=node;
}else if(this.left!=null && node.value<=this.value){
this.left.AddNode(node);
}else{
this.right.AddNode(node);
}
}
BinarySortTree类:public void BSTAddNode(BST_Node node)的定义
需要判断该类中代表二叉排序树的根节点的元素root是否为空,为空则对root直接赋值传入的node参数。否则通过root调用AddNode方法进行元素的添加。
public void BSTAddNode(BST_Node node){
if(root==null){
root=node;
}else{
root.AddNode(node);
}
}
BST_Demo类中main(String[] args):二叉排序树的创建:
import java.util.*;
public static void main(String[] args){
Random r=new Random();
int[] Array=new int[10];
for(int i=0;i<Array.length;i++){
Array[i]=r.nextInt(20);
}
System.out.println("原数组:");
System.out.println(Arrays.toString(Array));
BinarySortTree BST=new BinarySortTree();
for(int i:Array){
BST.BSTAddNode(new BST_Node(i));
}
}
至此二叉排序树创建完成!
中序遍历:
代码实现:
BST_Node类:
public void InorderTraversal(){
if(this.getLeft()!=null){
this.getLeft().InorderTraversal();
}
System.out.println(this.toString());
if(this.getRight()!=null){
this.getRight().InorderTraversal();
}
}
BinarySortTree类:
public void InorderTraversal(){
if(this.root==null){
System.out.println("当前二叉排序树为空无法遍历!");
}else{
this.root.InorderTraversal();
}
}
至此中序遍历完成!
删除结点:
先展示在BinarySortTree类中定义的删除结点的方法,具体情况代码之后有分析!
public void DeleteNode(int value)
public void DeleteNode(int value){
BST_Node target=SearchNode(value);
if(target==null){
System.out.println("未查找到需删除的结点!");
return;
}
BST_Node parent=SearchParent(target.getValue());
if(target.getLeft()==null && target.getRight()==null){
if(parent==null) {
this.root=null;
return;
}
if(parent.getRight()==target){
parent.setRight(null);
return;
}else{
parent.setLeft(null);
return;
}
}else if(target.getLeft()!=null && target.getRight()!=null){ //删除结点有两个子树
int RightMinValue=DeleteRightMin(target.getRight());
target.setValue(RightMinValue);
return;
}else{ //删除结点只有一个子树
if(parent==null){ //删除结点为根节点
if(this.root.getLeft()!=null){
this.root=root.getLeft();
return;
}else{
this.root=root.getRight();
return;
}
}
if(target.getLeft()!=null){ //删除结点非根节点且只存在左子结点
if(parent.getLeft()==target){
parent.setLeft(target.getLeft());
return;
}else{
parent.setRight(target.getLeft());
return;
}
}else{ //删除结点只存在右子节点
if(parent.getLeft()==target){
parent.setLeft(target.getRight());
return;
}else{
parent.setRight(target.getRight());
return;
}
}
}
}
三种情况
- 删除有两颗子树的结点
- 删除只有一颗子树的结点
- 删除没有子树的结点
以上三种情况的处理需要获取待删除结点以及该结点的父结点,故定义两方法
方法思路类似于前序遍历:
- 先考虑当前节点(this)是否为空
- 之后再根据查找值与当前结点值的大小关系分开讨论当前节点的左子节点和右子节点的情况
- 查找结点的值小于当前结点的值时,向左子树方向查找,但需要确定左子树是否为空。
- 查找结点的值大于当前结点的值时,向右子树方向查找,同样需要确定右子树是否为空。
方法一:public BSY_Node SearchNode(int value)
public BST_Node SearchNode(int value){
if(this.value==value){
return this;
}
if(value<this.value){
if(this.left==null){
return null;
}else{
return this.left.SearchNode(value);
}
}else{
if(this.right==null){
return null;
}else{
return this.right.SearchNode(value);
}
}
}
方法二:public BST_Node SearchParent(int value)
public BST_Node SearchParent(int value) {
if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
return this;
}
if (value < this.value) {
if (this.left == null) {
return null;
} else {
return this.left.SearchParent(value);
}
} else {
if (this.right == null) {
return null;
} else {
return this.right.SearchParent(value);
}
}
}
至此查找指定值的结点以及对应值的父结点方法完成!
1.删除有两颗子树的结点
因为删除结点之后的二叉排序树任需要具备其性质。当删除的是一个有两颗子树的结点时,可以视该结点为根结点与其两颗子树构成一棵二叉排序树。那么删除根结点,就可以通过搜索该根节点右子树中的最小结点使其代替根结点来完成。
注意:此时还需要考虑是否为根结点的情况,
理由:右子树中的最小结点作为根结点时,小于(可能等于)右子树中剩余结点,同时也大于左子树中的左右结点。
代替的具体操作:记录最小结点的值再将其删除,然后对根结点赋值所记录的
获取右子树中最小结点的元素并删除该节点:
public int DeleteRightMin(BST_Node RightRoot)
public int DeleteRightMin(BST_Node RightRoot){
BST_Node temp=RightRoot;
if(temp.getLeft()==null){
BST_Node parent=this.root.SearchParent(temp.getValue());
if(temp==RightRoot){
parent.setRight(temp.getRight());
return temp.getValue();
}
parent.setLeft(temp.getRight());
return temp.getValue();
}else{
return this.DeleteRightMin(temp.getLeft());
}
}
2.删除只有一颗子树的结点
需要考虑两种情况:1.为根结点 2.非根结点
原因:根结点无法获取其父结点
1.为根结点时(parent==null),判断其唯一子树是左子树还是右子树,并针对判断的不同进行不同操作。以根节点仅有左子树为例:如果根结点仅有左子树,此时需要删除根结点,只需要将根结点赋值根结点的左子结点。this.root=this.root.getLeft();仅有右子树同理。
2.为非根结点(else),判断待删除结点是其父结点的左子树还是右子树,同时还需要确定待删除结点所仅有的子树是左子树还是右子树,综上将有4种情况:
情况 | 应对方法 |
parent.getLeft()==target && target.getLeft()!=null | parent.setLeft(target.getLeft()) |
parent.getLeft()==target && target.getRight()!=null | parent.setLeft(target.getRight()) |
parent.getRight()==target && target.getLeft()!=null | Parent.setRight(target.getLeft()) |
parent.getRight()==target && target.getRight()!=null | Parent.setRight(target.getRight()) |
3.删除没有子树的结点
两种情况:1.没有子树的根结点 2.叶子结点
通过判断parent是否为空即可将两种情况分开if(parent==null)
1.没有子树的根结点,只需要将this.root==null即可。
2.叶子结点,需要考虑该叶子结点是其父结点的左子结点还是右子节点。如果是左子结点则parent.setLeft(null);
至此所有思路即其代码实现梳理完毕
所有代码:
1.BST_Demo.java
package 树实验.二叉排序树;
import java.util.Arrays;
public class BST_Demo {
public static void main(String[] args){
int[] Array={7,3,10,12,5,1,9};
System.out.println("原数组:");
System.out.println(Arrays.toString(Array)); //打印原数组
System.out.println("______________________________");
System.out.println("创建该数组的二叉排序树并中序遍历:");
BinarySortTree BST=new BinarySortTree();
for(int i:Array){ //通过遍历数组以及添加节点的方法创建二叉排序树
BST.BSTAddNode(new BST_Node(i));
}
BST.InorderTraversal(); //中序遍历二叉排序树
System.out.println();
BST.DeleteNode(3); //删除值为3的结点
BST.InorderTraversal();
}
}
2.BinarySortTree.java
package 树实验.二叉排序树;
public class BinarySortTree {
private BST_Node root;
public BinarySortTree(){
}
public BinarySortTree(BST_Node root){
this.root=root;
}
public BST_Node getRoot(){
return this.root;
}
public void setRoot(BST_Node root){
this.root=root;
}
public void BSTAddNode(BST_Node node){
if(root==null){
root=node;
}else{
root.AddNode(node);
}
}
public void InorderTraversal(){
if(this.root==null){
System.out.println("当前二叉排序树为空无法遍历!");
}else{
this.root.InorderTraversal();
}
}
//查找对应元素值的结点
public BST_Node SearchNode(int value){
if(root==null){
System.out.println("该二叉排序树为空无法查找!");
}
return root.SearchNode(value);
}
public BST_Node SearchParent(int value){
//两种情况没有父节点1.只有一个根节点 2.查找某结点的父结点,该结点就是根节点
if((this.root.getLeft()==null && this.root.getRight()==null) || this.root.getValue()==value){
return null;
}else{
return this.root.SearchParent(value);
}
}
/**
* @param RightRoot 右子树的根节点
* @return 查找到的最小右子数结点的值
*/
public int DeleteRightMin(BST_Node RightRoot){
BST_Node temp=RightRoot;
if(temp.getLeft()==null){
BST_Node parent=this.root.SearchParent(temp.getValue());
if(temp==RightRoot){
parent.setRight(temp.getRight());
return temp.getValue();
}
parent.setLeft(temp.getRight());
return temp.getValue();
}else{
return this.DeleteRightMin(temp.getLeft());
}
}
public void DeleteNode(int value){
BST_Node target=SearchNode(value);
if(target==null){
System.out.println("未查找到需删除的结点!");
return;
}
BST_Node parent=SearchParent(target.getValue());
if(target.getLeft()==null && target.getRight()==null){
if(parent==null) {
this.root=null;
return;
}
if(parent.getRight()==target){
parent.setRight(null);
return;
}else{
parent.setLeft(null);
return;
}
}else if(target.getLeft()!=null && target.getRight()!=null){ //删除结点有两个子树
int RightMinValue=DeleteRightMin(target.getRight());
target.setValue(RightMinValue);
return;
}else{ //删除结点只有一个子树
if(parent==null){ //删除结点为根节点
if(this.root.getLeft()!=null){
this.root=root.getLeft();
return;
}else{
this.root=root.getRight();
return;
}
}
if(target.getLeft()!=null){ //删除结点非根节点且只存在左子结点
if(parent.getLeft()==target){
parent.setLeft(target.getLeft());
return;
}else{
parent.setRight(target.getLeft());
return;
}
}else{ //删除结点只存在右子节点
if(parent.getLeft()==target){
parent.setLeft(target.getRight());
return;
}else{
parent.setRight(target.getRight());
return;
}
}
}
}
}
3.BST_Node.java
package 树实验.二叉排序树;
public class BST_Node {
private int value;
private BST_Node left;
private BST_Node right;
public BST_Node(int value){
this.value=value;
}
public int getValue(){
return this.value;
}
public void setValue(int value){
this.value=value;
}
public BST_Node getLeft() {
return left;
}
public void setLeft(BST_Node left) {
this.left = left;
}
public BST_Node getRight() {
return right;
}
public void setRight(BST_Node right) {
this.right = right;
}
public String toString(){
return "BST_Node[value="+this.value+"]";
}
public void AddNode(BST_Node node){
if(node==null) {
System.out.println("当前节点为空无法添加该节点!");
return;
}else {
if(node.value<=this.value){
if(this.left==null){
this.left=node;
}else{
this.left.AddNode(node);
}
}else{
if(this.right==null){
this.right=node;
}else{
this.right.AddNode(node);
}
}
}
}
public void InorderTraversal(){
if(this.left!=null){
this.left.InorderTraversal();
}
System.out.println(this.toString());
if(this.right!=null){
this.right.InorderTraversal();
}
}
public BST_Node SearchNode(int value){
if(this.value==value){
return this;
}
if(value<this.value){
if(this.left==null){
return null;
}else{
return this.left.SearchNode(value);
}
}else{
if(this.right==null){
return null;
}else{
return this.right.SearchNode(value);
}
}
}
public BST_Node SearchParent(int value) {
if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
return this;
}
if (value < this.value) {
if (this.left == null) {
return null;
} else {
return this.left.SearchParent(value);
}
} else {
if (this.right == null) {
return null;
} else {
return this.right.SearchParent(value);
}
}
}
}