相信大家对二叉树并不陌生,今天我们一起来学习一下 一个特殊的二叉树 —二叉排序树。
先来看百度给出的标准的定义:
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有节点的值均小于它的根节点的值;
(2)若右子树不空,则右子树上所有节点的值均大于它的根节点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的节点。
说的是非常的好,但是我一句没看懂,简单来说就是有序的二叉树,今天我们一起来实现它的增删改查
注意:下面实例中的代码是相关连的,可能会有调用,一定要看清楚o
数据定义
public class Node_tree {
private int val; //数据
private Node_tree lch;//左子树
private Node_tree rch;//右子树
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
public Node_tree getLch() {
return lch;
}
public void setLch(Node_tree lch) {
this.lch = lch;
}
public Node_tree getRch() {
return rch;
}
public void setRch(Node_tree rch) {
this.rch = rch;
}
/**
* 定义构造方法
*/
public Node_tree(){}
public Node_tree(int n){
this.val = n;
}
}
1、二叉排序树的添加
这个添加的方法很好懂,比如要插入一个数,从已经存在的二叉树的根节点可是判断,如果比节点的值大,则继续向其右子树进行比较,如果比节点的值小,则向其左子树进行比较,依次递归
/**
* 定义添加数据的方法,这不是静态方法,树的根节点调用,传入要插入节点
*/
public void add(Node_tree t){
//如果t的val值比这个节点的val值大,则往这个节点的右子树添加,不过前提是,右子树没有值,否则执行递归
if(t.getVal()>val){
if(rch==null){
rch=t;
}else{
rch.add(t);
}
}else{
//如果t的val值比这个节点的val值小,则往这个节点的左子树添加,不过前提是,左子树没有值,否则执行递归
if(lch==null){
lch=t;
}else{
lch.add(t);
}
}
}
2、二叉排序树的删除
我个人认为删除操作是最难的操作,里面用到的方法很多
(1)定义一个方法,传入一个树和一个数,如果这个数在这个树中,则返回这个数的节点对象
/**
* 查询某个数字的函数,返回值为这个数字的节点对象
* @param n 树的根节点,要查询的数字
*/
public static Node_tree check(Node_tree tree,int n){
Node_tree ndb = tree;
if(n>tree.val){
if(tree.getRch()!=null){
return ndb=check(tree.getRch(),n);
}else{
System.out.println("没有这个数");
}
}else if(n<tree.val){
if (tree.getLch()!=null){
return ndb = check(tree.getLch(),n);
}else{
System.out.println("没有这个数");
}
}else{
System.out.println("找到啦");
return ndb;
}
return ndb;
}
(2)因为删除操作需要,需要获得待删除节点的父亲节点,已经考虑根节点
/**
* 定义一个方法,找到一个节点的父节点,传入一个树的根节点和要找的孩子节点的value值
*/
public static Node_tree getparent(Node_tree node,int child){
Node_tree parent = new Node_tree();
while (node!=null){
if(node.getVal() == child){
break;
}
parent = node;
if (child<node.getVal()){
node = node.getLch();
}else{
node = node.getRch();
}
}
return parent;
}
(3)定义一个方法,来找到指定节点右子树中最小的数对应的节点,这一步非常重要,所以我将其单拿出来写,返回的节点就是用来替换待删除节点的,为后面提到的后继节点
/**
* 定一个方法,找到其右子树中最小的数,返回节点对象,s输入要查询的树就行
*/
public static Node_tree right_min(Node_tree tree){
Node_tree ndb = new Node_tree();
ndb = tree.rch;
while(ndb.getLch()!=null){
ndb=ndb.getLch();
}
return ndb ;
}
下面才真正开始进行删除操作
删除节点分为三种情况:
1、待删除节点有两个子节点
2、待删除节点有一个子节点
3、待删除节点没有子节点
*第二第三种情况可以放在一起说
步骤:
2.1、待删除节点有一个或者零个子节点
找到其右子树中最小的数对应的节点 作为后继节点— 》找到待删除节点的父亲节点—》判断待删除节点是父亲节点的左子树还是右子树—》将父亲节点对应的子节点指针指向后继节点
public static void del_one(Node_tree thisNode,Node_tree parent){
if(thisNode.getVal()>parent.getVal()){
//待删除节点是父节点的右子节点
if(thisNode.getRch()!=null){
parent.setRch(thisNode.getRch());
}else if(thisNode.getLch()!=null){
parent.setRch(thisNode.getLch());
}else{
parent.setRch(null);
}
}else{
//待删除节点是父节点的左子节点
if(thisNode.getRch()!=null){
parent.setRch(thisNode.getRch());
}else if(thisNode.getLch()!=null){
parent.setRch(thisNode.getLch());
}else{
parent.setLch(null);
}
}
}
2.2待删除节点有两个子节点
步骤:
找到后继节点(此处后继节点是右子树中最小的,所以一定没有左子树)—》找到后继节点的父亲节点p2—>先将后继节点的值赋给待删除节点—》让后继节点与其父亲节点进行同 2.1 的删除操作
public static void del(Node_tree node,int n){
//找到这个节点
Node_tree thisNode = check(node,n);
//找到这个节点的父节点
Node_tree parent = getparent(node,n);
//1、这个节点不存在的话,结束方法
if(thisNode == null){
return;
}
if(thisNode.getLch()!=null&&thisNode.getRch()!=null){
//待删除节点左右子节点都有
Node_tree rm = right_min(thisNode);
//获得这个后继节点的父亲
Node_tree rm_par = getparent(node,rm.getVal());
//将这个后继节点的值给待删除节点
thisNode.setVal(rm.getVal());
//对这个后继节点进行删除,因为它肯定是只有单个子节点或者没有
del_one(rm,rm_par);
return;
}else{
// 2、待删除节点有一个子节点
// 3、待删除节点没有子节点
del_one(thisNode,parent);
}
}
3、二叉排序树的改
这个修改是最简单操作的,基本思想就是想删除你想替换的节点,再添加你想要的更换为的数的节点
/**
* 修改函数,这 就简单啦,就是先进行删除节点,再插入节点
* 传入 修改的树,和要修改的数,修改为的数
*/
public static void update(Node_tree node,int n1,int n2){
//先将原来的数删除
del(node,n1);
//将n2插入
Node_tree n2node = new Node_tree(n2);
node.add(n2node);
}
4、二叉排序树的查
这里的查找函数没有准备很多,因为二叉排序树中,左子树<节点<右子树,所以利用中序查询是最好的方法,能让其有序输出
public void midtravl(){
if (lch!=null){
lch.midtravl();
}
System.out.println(val);
if (rch!=null){
rch.midtravl();
}
}