前言
在数据结构与算法中,每一种算法都有其优缺点。
顺序存储:不排序,查找比较困难;顺序存储:排序,删除插入困难
链式存储:无论是否排序,查找都比较困难。
基于以上三点,我们就有了二叉排序树。
二叉排序树
二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),也称二叉搜索树。二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
二叉排序树的构造
package demo9;
public class BinarySortTree {
Node root;
/*
* 向二叉排序树中添加节点
*/
public void add(Node node) {
//如果是一颗空树
if(root==null) {
root=node;
}else {
root.add(node);
}
}
/*
* 中序遍历二叉排序树
*/
public void midShow() {
if(root!=null) {
root.midShow(root);
}
}
package demo9;
public class Node {
int value;
Node left;
Node right;
public Node (int value) {
this.value=value;
}
/*
* 向子树中添加节点
*/
public void add(Node node) {
if(node==null) {
return;
}
//判断传入的节点的值比当前子树的根节点的值是大是小
//添加的节点比当前结点更小
if(node.value<this.value) {
//如果左节点为空
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);
}
}
}
/*
* 中序遍历二叉排序,z中序遍历二叉排序树结果为从小到大的顺序
*/
public void midShow(Node node) {
if(node==null) {
return;
}
midShow(node.left);
System.out.println(node.value);
midShow(node.right);
}
测试类:
public class TestBinarySortTree {
public static void main(String[] args) {
int [] arr=new int [] {7,3,10,12,5,1,9};
//创建一颗二叉排序树
BinarySortTree bst=new BinarySortTree();
//循环添加
for(int i:arr) {
bst.add(new Node(i));
}
//查看树中的值
bst.midShow();
经过上面的步骤,我们就成功的创建了一个二叉排序树了,可以通过简单的测试来判断树的建立是否正确,其中一个比较简单的方法就是通过中序遍历该树,由于二叉排序树的特点,中序遍历的结果应当是一系列从小到大排序好的值
获取树中的最小值以及最大值
由于二叉排序树的特点,其最小值必定在最左子树,最大值必然在最右子树,当然,如果是空树那就没有了,如果是只有跟节点的树,那么最小值以及最大值都是根节点本身
二叉排序树的查找
查找包含某一值的节点,查找相对简单,具体实现可看代码:
/*
* 查找节点
*/
public Node search(int value) {
if(root==null) {
return null;
}else {
return root.search(value);
}
}
/**
* 查找节点
*/
public Node search(int value) {
if(this.value==value){
return this;
}else if(value<this.value){
if(left==null) {
return null;
}
return left.search(value);
}else{
if(right==null) {
return null;
}
return right.search(value);
}
}
二叉排序树的删除
删除节点是二叉排序树最复杂的一个地方,主要是由于删除的时候,存在多种情况
被删除的节点没有左右子树
被删除的节点只有左子树
被删除的节点只有右子树
被删除的节点有左右子树
前三种情况比较好处理,直接令其父亲指向其孩子即可,最后一种比较复杂,直接看代码结合注释比较好理解:
/**
* 删除节点
*/
public void delete(int value) {
if(root==null) {
return;
}else {
//找到这个节点
Node target=search(value);
//如果没有这个节点
if(target==null) {
return;
}
//找到他的父节点
Node parent=searchParent(value);
//要删除的节点是叶子节点
if(target.left==null&&target.right==null) {
//要删除的节点是父节点的左子节点
if(parent.left.value==value) {
parent.left=null;
//要删除的节点是右节点
}else {
parent.right=null;
}
//要删除的节点有两个子节点
}else if(target.left!=null&&target.right!=null) {
//删除右子树中值最小的节点,取刚获取到该节点的值
int min=deleteMin(target.right);
//替换目标节点中的值
target.value=min;
//要删除的节点有一个左子节点或右字节点
}else {
if(target.left!=null) {
//要删除的节点是左子节点
if(parent.left.value==value) {
parent.left=target.left;
//要删除的节点是右节点
}else {
parent.right=target.right;
}
//右子节点
}else {
if(parent.right.value==value) {
parent.right=target.right;
//要删除的节点是右节点
}else {
parent.right=target.right;
}
}
}
}
}
/**
* 删除一颗树中最小的值的节点
* @param right
* @return
*/
private int deleteMin(Node node) {
Node target=node;
//递归向左找
while(target.left!=null) {
target=target.left;
}
//删除最小值的这个节点
delete(target.value);
return target.value;
}
/*
* 搜索父节点
*/
public Node searchParent(int value) {
if(root==null) {
return null;
}else {
return root.searchParent(value);
}
}
}
/**
* 搜索父节点
*/
public Node searchParent(int value) {
if((this.left!=null&&this.left.value==value)||(this.right!=null&&this.right.value==value)) {
return this;
}else {
if(this.value>value&&this.left!=null) {
return this.left.searchParent(value);
}else if(this.value<value&&this.right!=null){
return this.right.searchParent(value);
}
return null;
}
}
}
总结
主要回顾了二叉排序树的基本原理,理解起来并不复杂,并且通过代码实现的方式,学习了二叉排序树的创建、插入、查找和删除等操作。其中删除相应的节点可以说是比较复杂,也是最难理解的一个,在学习的过程中可以结合画图的方式以便于自己的理解。