(BST)搜索二叉树删除指定结点的实现(详解)

(BST)搜索二叉树–>删除操作的实现(详解)

以下是我个人的解决思路(如有错误可以私信指出)

本文的二叉树的结点的数据存储结构如下:

/*
	实际书写的时候访问权限最好为private  一键构造get and set方法
*/
class treeNode {
        // 结点存储的数据  (不一定是int 也可能是一个引用数据类型)
        public int val;
        // 存储该节点的左孩子的内存地址
        public treeNode l_child;
        // 存储该节点的右孩子的内存地址
        public treeNode r_child;
        //养成好习惯 将实例变量中的n引用设置为ull  
        public treeNode(){
            this.l_child=this.r_child=null;
        }
        public treeNode(int date){
            this.val=date;
        }
}

一,首先需要查找到需要删除的结点

(1).由于二叉树的结构天然就是一个可递归操作的结构,所以我们选择递归去查找需要删除的结点,在下列实现这个操作的代码中形参parents用于存储当前节点的前驱也就是该节点的父亲

private treeNode SearchDeleteElement(treeNode current,treeNode parent,int val){
        if(current==null){
        //如果递归到了这个地方  证明该二叉树不存在要删除的结点
            return null;
        }
        /*
        	类似于二分(折半)查找   他的效率在数据相当庞大时  是非常高的 时间复杂度为O(log n)
        	所以顾名思义叫做搜索二叉树嘛~~~~他的查询效率相当高
        */
        if(current.val>val){
        	/*			注意:(我们调用这个方法传入的current是根结点,我们要从根节点开始寻找)
        		根据搜索二叉树的性质:
        				(1)如果当前结点的值大于要删除结点的值那证明我要删除的结点在该节点的左边
        					所以我传入current的左孩子,反之传入右孩子。 
        					如果既不大于又不小于那就是等于,
        					则查找成功   进入else分支 
            */
            current.l_child=SearchDeleteElement(current.l_child,current,val);
        }else if (current.val<val){
            current.r_child=SearchDeleteElement(current.r_child,current,val);
        }else {
        	/*
        		当递归走到这里的时候证明查找到了删除的元素
            	当前的current就是我们要删除的结点  parent是他的双亲结点
            	重点****接下来的操作就是重组这个节点  然后返回重组好的结点,递归结束。
            */
            。。。。。。。。。。。。。。。。
        }
		//最终返回 该树的根节点(该树已经处理完毕)
        return current;
    }

二,分情况讨论查找到的要删除结点的左右孩子情况

1.有左孩子(右孩子有或者无都归为一个情况)

(1)则去寻找左孩子中的最大值(用于顶替要删除的结点)

2.无左孩子有右孩子

(1)则去寻找右孩子中的最小值(用于顶替要删除的结点)

3.左右孩子都没有

(3)返回null

三,讨论寻找到的最大值或者最小值结点的左右孩子情况

(1)该节点是叶子结点
(2)该节点不是叶子结点

四,如何去寻找这个最大值或者最小值

(1)如何找最大值

1.我们知道在搜索二叉树中右孩子结点永远大于左孩子和根结点,所以我们只需要递归的去找到最后那个右孩子,此右孩子即为最大的结点(起点为要删除结点的右孩子结点)

(2)如何找最小值

1.同样的道理,左孩子永远是最小的,所以我们递归的去找到最后一个左孩子结点,此节点即为最小的结点((起点为要删除结点的左孩子结点))

五,(以上就是大概的思路)有些细节部分 我们来看代码:

***以下代码并未考虑删除结点为根节点的情况

public class Delete {
    private treeNode Min;
    private treeNode Max;
    public static treeNode delete(treeNode treeNode,int val){
        //特殊情况 :当前树只有一个结点
        if(treeNode.l_child==null && treeNode.r_child==null){
            //恰好要删除的就是该节点   或者不是
            if(treeNode.val==val)return null;
            return treeNode;
        }
        return new Delete().SearchDeleteElement(treeNode,null,val);
    }
    
    //=====================================寻找==========================================================
    private treeNode SearchDeleteElement(treeNode current,treeNode parent,int val){
        if(current==null){
            return null;
        }
        if(current.val>val){
            current.l_child=SearchDeleteElement(current.l_child,current,val);
        }else if (current.val<val){
            current.r_child=SearchDeleteElement(current.r_child,current,val);
        }else {
            if(parent==null){
                if (current.l_child!=null){
                    /*	
                    	如果删除的根节点则parent==null
                    	进入此分支    这里并未做出处理
                    */
            }
            
            if(current.l_child!=null){
            	//删除结点存在左孩子
                return L_deletetreeNode(current);
            }else if (current.r_child!=null){
            	//删除结点存在右孩子(不存在左孩子)
                return R_deletetreeNode(current);
            }else{
            	//左右孩子都没有
                return null;
            }
        }
        return current;
    }
    //============================================替换=======================================================
    //删除的结点若存在左孩子
    public treeNode L_deletetreeNode(treeNode current){
    		//调用此方法去寻找最大值,并会返回删除好最大值结点的一棵树(也就是要删除结点的左树)
        treeNode replace=Max(current.l_child,current.l_child);
            if(Max==null){
                replace.r_child=current.r_child;
                return replace;
            }
            //这种情况是恰好 找到的最大值就是要删除结点的左孩子结点
            treeNode r_=current.r_child;	//保存删除结点的右孩子 用于后面的拼接
            current=Max;					//用找到的最大值结点顶替要删除的结点
            current.l_child=replace;		//左树拼接replace
            current.r_child=r_;				//右树拼接上面保存的r_
            return current;					//返回重组好的结点current		
            					//下面也是类似操作我不解释了
    }
    
    //删除的结点若存在右边孩子 (没有左孩子)
    public treeNode R_deletetreeNode(treeNode current){
    		//调用此方法去寻找最小值,并会返回删除好最小值结点的一棵树(也就是要删除结点的右树)
        treeNode replace=Min(current.r_child,current.r_child);
            if(Min==null){
                replace.l_child=current.l_child;
                return replace;
            }
            current=Min;
            current.r_child=replace;
            return current;
    }
    //============================================顶替=======================================================
    //寻找删除结点左树的最大值,用于顶替删除结点
    public treeNode Max(treeNode current,treeNode parent){
    
        if (current.r_child==null){
            if(parent.r_child==null){
            //如果传进来的current恰好没有右孩子则直接返回此结点,直接替换要删除结点就ok
                return current;
            }
            //找到最大值并赋值给实例变量Max
            Max=current;
            //注意此时我们不能返回NULL因为有可能你找到的最大值结点(也就是替换结点)他可能存在左孩子!!但是没有右孩子
            return current.l_child;
        }
       	//递归寻找最大值
        current.r_child=Max(current.r_child,current);
        //返回删除好最大值结点的树
        return current;
        			//下面操作也是类似的我就不解释了
    }
    //寻找删除结点右树的最小值  用于顶替删除结点
    public treeNode Min(treeNode current,treeNode parent){
        if(current.l_child==null){
            if(parent.l_child==null){
                return current;
            }
            Min=current;
            return current.r_child;
        }
        current.l_child=Min(current.l_child,current);
        return current;
    }
}

六,以下是需要测试的树(每一个结点都需要删除一遍来测试)

(1)如果你有了思路,最好自己写一遍然后自己亲自测试所有结点都删除一边,多换几个不一样的树同样去测试他,保证你的代码是没有bug的!至少是能扛得住测验的!!

以下是我的测试用的三颗树 你们可以复制粘贴参考以下(如何创建任意一颗树,一颗搜索二叉树你也需要掌握并且实现)
1.
在这里插入图片描述

	treeNode root = new treeNode(8);
    treeNode root_l_child = new treeNode(3);
    treeNode root_l_child_l_child = new treeNode(1);
    treeNode root_l_child_r_child= new treeNode(6);
    treeNode root_l_child_r_child_l_child = new treeNode(4);
    treeNode root_l_child_r_child_r_child = new treeNode(7);
    treeNode root_r_child = new treeNode(10);
    treeNode root_r_child_r_child = new treeNode(14);
    treeNode root_r_child_r_child_l_child = new treeNode(13);
    root.l_child = root_l_child; root.r_child = root_r_child;
    root.l_child.l_child = root_l_child_l_child;
    root.l_child.r_child = root_l_child_r_child;
    root.l_child.r_child.l_child = root_l_child_r_child_l_child;
    root.l_child.r_child.r_child = root_l_child_r_child_r_child;
    root.r_child.r_child = root_r_child_r_child;
    root.r_child.r_child.l_child = root_r_child_r_child_l_child;

在这里插入图片描述

treeNode root=new treeNode(1);
treeNode root_r_child=new treeNode(100);
treeNode root_r_child_l_child=new treeNode(50);
treeNode root_r_child_l_child_r_child=new treeNode(70);
treeNode root_r_child_l_child_r_child_l_child=new treeNode(60);
root.r_child=root_r_child;
root.r_child.l_child=root_r_child_l_child;
root.r_child.l_child.r_child=root_r_child_l_child_r_child;
root.r_child.l_child.r_child.l_child=root_r_child_l_child_r_child_l_child;

在这里插入图片描述

   treeNode root=new treeNode(100);
  treeNode root_l_child=new treeNode(50);
  treeNode root_l_child_l_child=new treeNode(20);
  treeNode root_l_child_l_child_r_child=new treeNode(30);
  treeNode root_l_child_l_child_r_child_r_child=new treeNode(45);
  treeNode root_l_child_l_child_l_child=new treeNode(10);
  treeNode root_l_child_l_child_l_child_l_child=new treeNode(5);
  root.l_child=root_l_child;
  root.l_child.l_child=root_l_child_l_child;
  root.l_child.l_child.r_child=root_l_child_l_child_r_child;
  root.l_child.l_child.r_child.r_child=root_l_child_l_child_r_child_r_child;
  root.l_child.l_child.l_child=root_l_child_l_child_l_child;
  root.l_child.l_child.l_child.l_child=root_l_child_l_child_l_child_l_child;
二叉排序二叉链表存储结构的类型定义如下: typedef struct node{ int data; //用整数表示一个结点的名 struct node *LChild,*RChild; //左右指针域 }BSTNode,*BSTree; 设计算法并编写程序求解以下几个问题。 8 12 14 10 7 3 15 6 2 4 1 5 11 9 13 16 13 (1)键盘输入一个元素序列创建一棵二叉排序,输出该二叉排序的中序遍历序列; 例如,若输入 45,24,55,12,37,53,60,23,40,70 则创建的二叉排序为: 输出结果为:12 23 24 37 40 45 53 55 60 70 (2)在(1)中所得的二叉排序树中插入一个值为 58 的结点,再输出它的中序遍历序列,输出 结果为:12 23 24 37 40 45 53 55 58 60 70 (3)在(1)中所得的二叉排序树中删除值为 45 的结点,再输出它的中序遍历序列,输出结果 为:12 23 24 37 40 53 55 58 60 70 (4)利用(1)中所得的二叉排序的所有叶子结点构造一个带头结点的单链表 L。要求不能 破坏这棵二叉排序。所得的单链表 L 如下。 输出该链表各结点的值,输出结果为:23 40 53 70 (5)设计算法将(1)中所得的二叉排序的左右子进行交换,由于二叉树是一种递归定义, 所以子的左右两棵子也要相交换,依此类推。最后输出所得到的二叉树的中序遍历序列。 例如,经过上述操作后,(1)中所得的二叉排序变为如下形式。 输出该二叉树的中序序列,结果为:70 60 55 53 45 40 37 24 23 12 (6)设计算法统计并输出(1)中所得的二叉排序树中只有一个孩子结点结点个数。输出结 果为:3(7)在(1)中所得的二叉排序树中设计算法并编写程序输出结点 40 的所有祖先结点。输 出结果为:45 24 37
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值