二分搜索树节点删除(Java 实例代码)

news2024/12/24 21:25:48

目录

 

二分搜索树节点删除

src/runoob/binary/BSTRemove.java 文件代码:


 

二分搜索树节点删除

本小节介绍二分搜索树节点的删除之前,先介绍如何查找最小值和最大值,以及删除最小值和最大值。

以最小值为例(最大值同理):

查找最小 key 值代码逻辑,往左子节点递归查找下去:

...
// 返回以node为根的二分搜索树的最小键值所在的节点
private Node minimum(Node node){
    if( node.left == null )
        return node;

    return minimum(node.left);
}
...

删除二分搜索树的最小 key 值,如果该节点没有右子树,那么直接删除,如果存在右子树,如图所示:

 

aa5c4a304a1fc4b60d1f3f195e995afb.png

删除节点 22,存在右孩子,只需要将右子树 33 节点代替节点 22。

 

b969a5bf94550dfa08be57b5554ec91b.png

这个删除最小值用代码表示:

...
// 删除掉以node为根的二分搜索树中的最小节点
// 返回删除节点后新的二分搜索树的根
private Node removeMin(Node node){

    if( node.left == null ){

        Node rightNode = node.right;
        node.right = null;
        count --;
        return rightNode;
    }

    node.left = removeMin(node.left);
    return node;
}
...

现在讨论二分搜索树节点删除分以下三种情况:

1、删除只有左孩子的节点,如下图节点 58。

 

68178dee7ebf498bd269496e27c028d5.png

删除掉元素 58,让左子树直接代替 58 的位置,整个二分搜索树的性质不变。

 

eb0985a8d1a67274fd175ffedfdefd1a.png

2、删除只有右孩子的节点,如下图节点 58。

 

98e6ce71248c57d89d083c8eccabb1ab.png

删除掉元素 58,让右子树直接代替 58 的位置,整个二分搜索树的性质不变。

 

72c61c468c10765aa8157785e96e6e7b.png

3、删除左右都有孩子的节点,如下图节点 58。

 

a673f9d7eb486373f3a30a00d0a196d0.png

(1)找到右子树中的最小值,为节点 59

 

618c6a1b64d04152586a31b2ad28781c.png

(2)节点 59 代替待删除节点 58

 

01cf87db74a8a7643e2ffb40d4376db9.png

综合以上规律,删除以 node 为根的二分搜索树中键值为 key 的节点,核心代码示例:

源码包下载:Downloadhttps://www.runoob.com/wp-content/uploads/2020/09/runoob-algorithm-BSTRemove.zip

src/runoob/binary/BSTRemove.java 文件代码:

package runoob.binary;

import java.util.LinkedList;

/**
 * 二分搜索树节点删除
 */
public class BSTRemove<Key extends Comparable<Key>, Value>  {
    // 树中的节点为私有的类, 外界不需要了解二分搜索树节点的具体实现
    private class Node {
        private Key key;
        private Value value;
        private Node left, right;

        public Node(Key key, Value value) {
            this.key = key;
            this.value = value;
            left = right = null;
        }

        public Node(Node node){
            this.key = node.key;
            this.value = node.value;
            this.left = node.left;
            this.right = node.right;
        }
    }

    private Node root;  // 根节点
    private int count;  // 树种的节点个数

    // 构造函数, 默认构造一棵空二分搜索树
    public BSTRemove() {
        root = null;
        count = 0;
    }

    // 返回二分搜索树的节点个数
    public int size() {
        return count;
    }

    // 返回二分搜索树是否为空
    public boolean isEmpty() {
        return count == 0;
    }

    // 向二分搜索树中插入一个新的(key, value)数据对
    public void insert(Key key, Value value){
        root = insert(root, key, value);
    }

    // 查看二分搜索树中是否存在键key
    public boolean contain(Key key){
        return contain(root, key);
    }

    // 在二分搜索树中搜索键key所对应的值。如果这个值不存在, 则返回null
    public Value search(Key key){
        return search( root , key );
    }

    // 二分搜索树的前序遍历
    public void preOrder(){
        preOrder(root);
    }

    // 二分搜索树的中序遍历
    public void inOrder(){
        inOrder(root);
    }

    // 二分搜索树的后序遍历
    public void postOrder(){
        postOrder(root);
    }

    // 二分搜索树的层序遍历
    public void levelOrder(){

        // 我们使用LinkedList来作为我们的队列
        LinkedList<Node> q = new LinkedList<Node>();
        q.add(root);
        while( !q.isEmpty() ){

            Node node = q.remove();

            System.out.println(node.key);

            if( node.left != null )
                q.add( node.left );
            if( node.right != null )
                q.add( node.right );
        }
    }

    // 寻找二分搜索树的最小的键值
    public Key minimum(){
        assert count != 0;
        Node minNode = minimum( root );
        return minNode.key;
    }

    // 寻找二分搜索树的最大的键值
    public Key maximum(){
        assert count != 0;
        Node maxNode = maximum(root);
        return maxNode.key;
    }

    // 从二分搜索树中删除最小值所在节点
    public void removeMin(){
        if( root != null )
            root = removeMin( root );
    }

    // 从二分搜索树中删除最大值所在节点
    public void removeMax(){
        if( root != null )
            root = removeMax( root );
    }

    // 从二分搜索树中删除键值为key的节点
    public void remove(Key key){
        root = remove(root, key);
    }

    //********************
    //* 二分搜索树的辅助函数
    //********************

    // 向以node为根的二分搜索树中, 插入节点(key, value), 使用递归算法
    // 返回插入新节点后的二分搜索树的根
    private Node insert(Node node, Key key, Value value){

        if( node == null ){
            count ++;
            return new Node(key, value);
        }

        if( key.compareTo(node.key) == 0 )
            node.value = value;
        else if( key.compareTo(node.key) < 0 )
            node.left = insert( node.left , key, value);
        else    // key > node->key
            node.right = insert( node.right, key, value);

        return node;
    }

    // 查看以node为根的二分搜索树中是否包含键值为key的节点, 使用递归算法
    private boolean contain(Node node, Key key){

        if( node == null )
            return false;

        if( key.compareTo(node.key) == 0 )
            return true;
        else if( key.compareTo(node.key) < 0 )
            return contain( node.left , key );
        else // key > node->key
            return contain( node.right , key );
    }

    // 在以node为根的二分搜索树中查找key所对应的value, 递归算法
    // 若value不存在, 则返回NULL
    private Value search(Node node, Key key){

        if( node == null )
            return null;

        if( key.compareTo(node.key) == 0 )
            return node.value;
        else if( key.compareTo(node.key) < 0 )
            return search( node.left , key );
        else // key > node->key
            return search( node.right, key );
    }

    // 对以node为根的二叉搜索树进行前序遍历, 递归算法
    private void preOrder(Node node){

        if( node != null ){
            System.out.println(node.key);
            preOrder(node.left);
            preOrder(node.right);
        }
    }

    // 对以node为根的二叉搜索树进行中序遍历, 递归算法
    private void inOrder(Node node){

        if( node != null ){
            inOrder(node.left);
            System.out.println(node.key);
            inOrder(node.right);
        }
    }

    // 对以node为根的二叉搜索树进行后序遍历, 递归算法
    private void postOrder(Node node){

        if( node != null ){
            postOrder(node.left);
            postOrder(node.right);
            System.out.println(node.key);
        }
    }

    // 返回以node为根的二分搜索树的最小键值所在的节点
    private Node minimum(Node node){
        if( node.left == null )
            return node;

        return minimum(node.left);
    }

    // 返回以node为根的二分搜索树的最大键值所在的节点
    private Node maximum(Node node){
        if( node.right == null )
            return node;

        return maximum(node.right);
    }

    // 删除掉以node为根的二分搜索树中的最小节点
    // 返回删除节点后新的二分搜索树的根
    private Node removeMin(Node node){

        if( node.left == null ){

            Node rightNode = node.right;
            node.right = null;
            count --;
            return rightNode;
        }

        node.left = removeMin(node.left);
        return node;
    }

    // 删除掉以node为根的二分搜索树中的最大节点
    // 返回删除节点后新的二分搜索树的根
    private Node removeMax(Node node){

        if( node.right == null ){

            Node leftNode = node.left;
            node.left = null;
            count --;
            return leftNode;
        }

        node.right = removeMax(node.right);
        return node;
    }

    // 删除掉以node为根的二分搜索树中键值为key的节点, 递归算法
    // 返回删除节点后新的二分搜索树的根
    Node remove(Node node, Key key){

        if( node == null )
            return null;

        if( key.compareTo(node.key) < 0 ){
            node.left = remove( node.left , key );
            return node;
        }
        else if( key.compareTo(node.key) > 0 ){
            node.right = remove( node.right, key );
            return node;
        }
        else{   // key == node->key

            // 待删除节点左子树为空的情况
            if( node.left == null ){
                Node rightNode = node.right;
                node.right = null;
                count --;
                return rightNode;
            }

            // 待删除节点右子树为空的情况
            if( node.right == null ){
                Node leftNode = node.left;
                node.left = null;
                count--;
                return leftNode;
            }

            // 待删除节点左右子树均不为空的情况

            // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
            // 用这个节点顶替待删除节点的位置
            Node successor = new Node(minimum(node.right));
            count ++;

            successor.right = removeMin(node.right);
            successor.left = node.left;

            node.left = node.right = null;
            count --;

            return successor;
        }
    }
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1002319.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在 Simscape Electrical 中对两区 MVDC 电动船的建模和仿真(Simulink实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Linux Centos7内网服务器离线升级openssh9.3

内网服务器需要升级openssh&#xff0c;被折磨了一整天&#xff0c;觉得有必要记录一下&#xff0c;不然对不起这差点崩溃的一天&#xff0c;主要的几个难点就是不能yum一键安装&#xff0c;需要自己找到对应的依赖版本然后通过堡垒机上传到内网&#xff0c;还有就是服务器很干…

无涯教程-JavaScript - ISPMT函数

描述 ISPMT函数计算在特定投资期间支付的利息。提供此功能是为了与Lotus 1-2-3兼容。 语法 ISPMT (rate, per, nper, pv)争论 Argument描述Required/OptionalRateThe interest rate for the investment.RequiredPerThe period for which you want to find the interest, an…

vue-puzzle-vcode完成验证码拖拽

一、 vue-puzzle-vcode插件【推荐】 GitHub地址&#xff1a;beeworkshop/vue-puzzle-vcode 1、安装vue-puzzle-vcode cnpm i -S vue-puzzle-vcode2、实现代码 <template><div><Vcode :show"isShow" success"success" close"close&…

7年经验之谈 —— Web测试是什么,有何特点?

Web测试是指对Web应用程序进行验证和评估的过程&#xff0c;以确保其功能、性能和安全性符合预期。 Web测试具体包括以下几个方面的内容&#xff1a; 功能测试&#xff1a;验证Web应用程序是否按照需求规格说明书中定义的功能正常工作。功能测试包括输入验证、表单提交、页面…

【Flutter】Flutter 使用 pull_to_refresh 实现下拉刷新和上拉加载

【Flutter】Flutter 使用 pull_to_refresh 实现下拉刷新和上拉加载 文章目录 一、前言二、pull_to_refresh 包简介三、安装与基本使用四、高级功能与配置五、实际业务中的用法六、完整示例七、总结 一、前言 你好&#xff01;在移动开发中&#xff0c;下拉刷新和上拉加载是非常…

【数据结构】双向链表详解

当我们学习完单链表后&#xff0c;双向链表就简单的多了&#xff0c;双向链表中的头插&#xff0c;尾插&#xff0c;头删&#xff0c;尾删&#xff0c;以及任意位置插&#xff0c;任意位置删除比单链表简单&#xff0c;今天就跟着小张一起学习吧&#xff01;&#xff01; 双向链…

Pytorch Advanced(一) Generative Adversarial Networks

生成对抗神经网络GAN&#xff0c;发挥神经网络的想象力&#xff0c;可以说是十分厉害了 参考 1、AI作家 2、将模糊图变清晰(去雨&#xff0c;去雾&#xff0c;去抖动&#xff0c;去马赛克等)&#xff0c;这需要AI具有“想象力”&#xff0c;能脑补情节&#xff1b; 3、进行数…

JavaScript Promise 的真正工作原理

Promise 是处理异步代码的一种技术,也称为脱离回调地狱的头等舱门票。 3 承诺状态 待定状态 已解决状态 拒绝状态 理解 JavaScript Promis 什么是承诺? 通常,承诺被定义为最终可用的值的代理。 Promise 多年来一直是 JavaScript 的一部分(在 ES2015 中标准化并引入)。最…

【数据结构】前言概况 - 树

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;数据结构 &#x1f525;该文章针对树形结构作出前言&#xff0c;以保证可以对树初步认知。 目录&#xff1a; &#x1f30d;前言:&#x1f3…

Python语义分割与街景识别(4):程序运行

前言 本文主要用于记录我在使用python做图像识别语义分割训练集的过程&#xff0c;由于在这一过程中踩坑排除BUG过多&#xff0c;因此也希望想做这部分内容的同学们可以少走些弯路。 本文是python语义分割与街景识别第四篇&#xff0c;关于程序的内容&#xff0c;也是差不多最…

【Unity编辑器扩展】| GameView面板扩展

前言【Unity编辑器扩展】| GameView面板扩展未运行时在Game视图进行绘制总结前言 前面我们介绍了Unity中编辑器扩展的一些基本概念及基础知识,还有编辑器扩展中用到的相关特性Attribute介绍。后面就来针对Uniity编辑器扩展中比较常用的模块进行学习介绍。本文就来详细介绍一下…

JAVA版的数据结构——链表

目录 1.单向不带头链表 1.1 链表的概念及结构 1.2 代码部分 1.3 完整的全部代码 2. 双向不带头链表 2.1 代码部分 2.2 完整的代码 3. MySingleList与MyLinkedList代码上的区别 4. LinkedList的使用 4.1 什么是LinkedList 4.2 LinkedList的使用 4.2.1 LinkedList的构…

【数据结构】堆的向上调整和向下调整以及相关方法

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3; 文章目录 一、堆的概念二、堆的性质…

github上创建分支并合并到master

github上创建分支并合并到master 目录概述需求&#xff1a; 设计思路实现思路分析1.创建分支2.commit changes3.create pull request按钮4.网页解析器5.数据处理器 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,ful…

[deeplearning]深度学习框架torch的概念以及数学内容

&#xff08;提前声明&#xff1a;这边的操作系统为ubuntn22.04,至于window上如何进行安装和导入按这边不是很理解&#xff09; &#xff08;另外代码样例基本不使用notebook&#xff0c;paddle等等在线工具&#xff0c;而是使用本机安装好的python环境&#xff0c;和pytorch框…

IDEA中maven的设置以及相关功能

Maven 项目介绍 学习前提 相对于传统的项目&#xff0c;Maven 下管理和构建的项目真的非常好用和简单&#xff0c;所以这里也强调下&#xff0c;尽量使用此类工具进行项目构建。 ## Maven 常用设置介绍 如上图标注 1 所示&#xff0c;我们可以指定我们本地 Maven 的安装目录…

模块化开发_groupby查询think PHP5.1

要求按照分类的区别打印出不同类别的数据计数 如张三&#xff0c;做了6件事情 这里使用原生查询先测试 SELECT cate_id, COUNT(*) AS order_count FROM tp_article GROUP BY cate_id;成功 然后项目中实现 public function ss(){$sql "SELECT cate_id, COUNT(*) AS orde…

RCNA 锐捷培训

第一章 网络基础入门 1.1 OSI参考模型及TCP/IP协议栈 数据是如何传输的&#xff1f; 数据在计算机网络中传输通常依赖于TCP/IP协议模型。 什么是网络&#xff1f; 网络是一种连接多个计算机、设备或系统的通信基础设施&#xff0c;其目的是实现资源共享、信息传递、接收和共享…

14.Xaml ProgressBar控件 进度条控件

1.运行效果 2.运行源码 a.Xaml源码 <Grid Name="Grid1"><!--Orientation="Horizontal" 进度条的方向 水平的还是垂直的Value="40" 进度的数值Minimum="0" 最小值Maximum