数据结构-二分搜索树(Binary Search Tree)

news2025/5/16 20:39:52

一,简单了解二分搜索树

树结构:

问题:为什么要创造这种数据结构

1,树结构本身是一种天然的组织结构,就好像我们的文件夹一样,一层一层的.


2,树结构可以更高效的处理问题

二,二分搜索树的基础

1、二叉树
 

2,二叉树的重要特性

满二叉树

总结:

1. 叶子结点出现在二叉树的最底层,除叶子结点之外的其它结点都有两个孩子结点。
2. 一个层数为k 的满二叉树总结点数为:
3. 第i层上的结点数为:
4. 一个层数为k的满二叉树的叶子结点个数(也就是最后一层):
4、二叉树不一定是“满”的

3,二分搜索树

(注意:存储的元素必须具有可比性)

1,向二分搜索树添加元素

2,向二分搜索树查询操作

1,递归终止的条件 : if(node == null ) return false;
2,递归操作
if (ele.compareTo(node.ele) < 0) {
return search(node.left, ele);
} else if (ele.compareTo(node.ele) > 0) {
return search(node.right, ele);
} else {
return true;
}

3,二分搜索树的遍历操作

遍历操作:把树中所有节点都访问一遍

1前序遍历,

2中序遍历,

3后序遍历

4层序遍历(广度优先)

(代码,会在后面一起展现.)

4,二分搜索树寻找最大值,最小值

同样的原理找出二分搜素树中最大的元素,这里不在过多赘述.

5,删除操作
情况一:(叶子结点)

情况二、(非叶子结点)

删除后


6,删除二分搜索树中的节点

情况一:
 

情况二、
 

情况三:左右孩子都不为空的情况

使用Hibbard Deletion
 

三,用代码实现二分搜索树.实现相关功能.

(由于功能实现较多,代码较长)

其中包含是前面的各种功能操作的实现,包括,前,中,后,层,序把遍历,删除,添加,等等操作.需要的同学可以仔细查看.

mport java.nio.channels.Pipe;
import java.util.*;
import java.util.stream.Collectors;

// 二分搜索树
public class BinarySearchTree<T extends Comparable<T>> {
    // 定义树的结点
    public class Node {
        T val;
        Node left; //  左孩子
        Node right; // 右孩子

        public Node(T val) {
            this.val = val;
        }
    }

    // 定义树的根
    private Node root;// 树根

    // 统计树中结点的个数
    private int size;// 树中结点的个数

    public BinarySearchTree() {
        this.root = null;
        this.size = 0;
    }

    // 判断树是否为空
    public boolean isEmpty() {
        return this.size == 0;
    }

    // 获取树中元素的个数
    public int getSize() {
        return this.size;
    }

    // 向树中添加元素
    public void add(T val) {
        this.size++;
        this.root = add(this.root, val);
    }

    /**
     * 添加的递归方法
     *
     * @param node 树的根结点
     * @param val  添加的值
     */
    private Node add(Node node, T val) {
        //递归终止的条件
        if (node == null) {
            Node leafNode = new Node(val);
            return leafNode;
        }
        // 递归操作
        if (node.val.compareTo(val) > 0) {
            node.left = add(node.left, val);
        } else {
            node.right = add(node.right, val);
        }
        return node;
    }

    // 将树中所有结点进行遍历--中序遍历( 深度优先搜索 DFS,使用的栈来实现)
    public String middleTravel() {
        List<T> result = new ArrayList<>();
        middleTravel(this.root, result);
        return result.stream().map(item -> item.toString()).collect(Collectors.joining(","));
    }

    /**
     * 中序遍历
     *
     * @param node 树的根结点
     */
    private void middleTravel(Node node, List<T> result) {
        // 递归终止的条件
        if (node == null) {
            return;
        }
        // 递归操作
        // 先遍历左子树
        middleTravel(node.left, result);
        //  打印当前值
        result.add(node.val);
        //  再遍历右子树
        middleTravel(node.right, result);
    }

    public String beforeTravel() {
        List<T> result = new ArrayList<>();
        beforeTravel(this.root, result);
        return result.stream().map(item -> item.toString()).collect(Collectors.joining(","));
    }

    /**
     * 前序遍历
     *
     * @param node 树的根结点
     */
    private void beforeTravel(Node node, List<T> result) {
        // 递归终止的条件
        if (node == null) {
            return;
        }
        // 递归操作
        //  打印当前值
        result.add(node.val);
        // 先遍历左子树
        beforeTravel(node.left, result);
        //  再遍历右子树
        beforeTravel(node.right, result);
    }


    public String afterTravel() {
        List<T> result = new ArrayList<>();
        afterTravel(this.root, result);
        return result.stream().map(item -> item.toString()).collect(Collectors.joining(","));
    }

    /**
     * 后序遍历
     *
     * @param node 树的根结点
     */
    private void afterTravel(Node node, List<T> result) {
        // 递归终止的条件
        if (node == null) {
            return;
        }
        // 递归操作
        // 先遍历左子树
        afterTravel(node.left, result);
        //  再遍历右子树
        afterTravel(node.right, result);
        //  打印当前值
        result.add(node.val);
    }

    // 查找的方法
    public boolean contains(T val) {
        return contains(this.root, val);
    }

    /**
     * 从以node为根的二分搜索树中查找元素val
     *
     * @param node 根节点
     * @param val  要搜索的值
     * @return
     */
    private boolean contains(Node node, T val) {
        // 递归到底的情况
        if (node == null) {
            return false;
        }
        // 递归操作
        if (node.val.compareTo(val) == 0) {
            return true;
        } else if (node.val.compareTo(val) > 0) {
            return contains(node.left, val);
        } else {
            return contains(node.right, val);
        }
    }

    // 树的层序遍历(广度优先搜索  BFS,使用队列来实现)
    public String levelTravel() {
        List<String> list = new ArrayList<>();
        // 1、 创建一个队列
        Queue<AbstractMap.SimpleEntry<Integer, Node>> queue = new LinkedList<>();
        // 2、将根结点入入队
        if (this.root != null) {
            queue.offer(new AbstractMap.SimpleEntry<>(1, this.root));
        }
        // 3、遍历队列
        while (!queue.isEmpty()) {
            AbstractMap.SimpleEntry<Integer, Node> temp = queue.poll();
            Node value = temp.getValue();
            int key = temp.getKey();
            //3-1  先将内容保存
            list.add(value.val.toString() + "------" + key);
            // 3-2  判断左子树是否为空,不为空就入队
            if (value.left != null) {
                queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.left));
            }
            // 3-3 判断右子树是否为空,不为空就入队
            if (value.right != null) {
                queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.right));
            }
        }

        return list.stream().collect(Collectors.joining(","));
    }

    public List<List<T>> levelOrder() {
        // 返回值类型是啥,就创建啥
        List<List<T>> result = new ArrayList<>();
        // 1、 创建一个队列
        Queue<AbstractMap.SimpleEntry<Integer, Node>> queue = new LinkedList<>();
        // 2、将根结点入入队
        if (this.root != null) {
            queue.offer(new AbstractMap.SimpleEntry<>(1, this.root));
        }
        while (!queue.isEmpty()) {
            AbstractMap.SimpleEntry<Integer, Node> temp = queue.poll();
            Node value = temp.getValue();
            int key = temp.getKey();
            //3-1  先将内容保存
            if(result.get(key-1)==null){
                result.add(new ArrayList<>());
            }
            result.get(key-1).add(value.val);

            // 3-2  判断左子树是否为空,不为空就入队
            if (value.left != null) {
                queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.left));
            }
            // 3-3 判断右子树是否为空,不为空就入队
            if (value.right != null) {
                queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.right));
            }
        }
        return result;
    }

    // Pair对
    public class Pair<Node> {
        private Node value; // 保存值
        private int key; // 保存层

        public Pair(Node value, int key) {
            this.value = value;
            this.key = key;
        }

        public Node getValue() {
            return value;
        }

        public int getKey() {
            return key;
        }
    }

    // 从二分搜索树中找最小值(在整棵树的最左边)
    public T getMinVal() {
        // 判断树是否为空
        if (this.root == null) {
            return null;
        }
        Node curNode = this.root;
        while (curNode.left != null) {
            curNode = curNode.left;
        }
        return curNode.val;
    }

    public T getMinValDG() {
        // 判断树是否为空
        if (this.root == null) {
            return null;
        }
        return getMinValDG(this.root).val;
    }

    /**
     * 从以node为根的二分搜索树中查找最小值
     *
     * @param node 树的根节点
     */
    private Node getMinValDG(Node node) {
        //递归终止的条件
        if (node.left == null) {
            return node;
        }
        // 递归操作
        return getMinValDG(node.left);
    }


    // 从二分搜索树中找最 大值(在整棵树的最右边)
    public T getMaxVal() {
        // 判断树是否为空
        if (this.root == null) {
            return null;
        }
        Node curNode = this.root;
        while (curNode.right != null) {
            curNode = curNode.right;
        }
        return curNode.val;
    }

    public T getMaxValDG() {
        // 判断树是否为空
        if (this.root == null) {
            return null;
        }
        return getMaxValDG(this.root).val;
    }

    private Node getMaxValDG(Node node) {
        //递归终止的条件
        if (node.right == null) {
            return node;
        }
        // 递归操作
        return getMinValDG(node.right);
    }


    // 从以this.root为根的二分搜索树中删除最小的结点
    public void removeMinNode() {
        if (this.root == null) {
            return;
        }
        this.root = removeMinNode(this.root);
    }

    /**
     * 从以node为根的二分搜索树中删除最小的节点
     *
     * @param node 树的根节点
     * @return 删除之后的树的根节点
     */
    private Node removeMinNode(Node node) {
        // 递归终止的条件
        if (node.left == null) {
            this.size--;
            return node.right;
        }
        // 递归操作
        node.left = removeMinNode(node.left);
        return node;
    }


    // 删除操作
    public void remove(T val) {
        if (!contains(val)) {
            return;
        }
        this.root = remove(this.root, val);
    }

    /**
     * 从以node为根的二分搜索树中删除元素val的节点
     *
     * @param node 树的根节点
     * @param val  删除的值
     * @return
     */
    private Node remove(Node node, T val) {
        // 递归终止的条件
        if (node == null) {
            return null;
        }
        if (node.val.compareTo(val) == 0) {
            // 更新size
            this.size--;
            if (node.right == null) {
                //1、右子树为空
                return node.left;
            } else if (node.left == null) {
                //2、左子树为空
                return node.right;
            } else {
                // 3、左右子树都不为空
                // 3-1 找到删除节点的后继
                Node suffixNode = getMinValDG(node.right);
                // 3-2 更新suffixNode的左右子树
//                suffixNode.right = removeMinNode(node.right);
                suffixNode.right = remove(node.right, getMinValDG(node.right).val);
                suffixNode.left = node.left;
                this.size++;
                // 3-3 返回suffixNode
                return suffixNode;
            }
        }
        // 递归操作
        if (node.val.compareTo(val) > 0) {
            node.left = remove(node.left, val);
        } else {
            node.right = remove(node.right, val);
        }
        return node;
    }}

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

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

相关文章

Python习题详解

练习&#xff1a; 1&#xff0c;计算100以内奇数的和 #计算100以内所有奇数的和 sum 0 # n 1 # while n < 100: # # sum sum n # sum n # # n n 2 # n 2 # print(sum) n 99 #求偶数时n 100 while n > 0:sum n# n n - 2n - 2 print(sum)2&#xff0c;打印直…

【多线程】多线程案例——单例模式详解(包含懒汉模式,饿汉模式)

单例模式 &#x1f334;饿汉模式&#x1f38d;懒汉模式&#x1f338;单线程版&#xff08;线程不安全&#xff09;&#x1f338;多线程版&#x1f338;多线程版(改进) ⭕总结 单例模式是校招中最常考的 设计模式之⼀. 啥是设计模式? 设计模式好⽐象棋中的 “棋谱”. 红⽅当头…

AtCoder ABC342 A-D题解

华为出的比赛&#xff1f; 好像是全站首个题解哎&#xff01; 比赛链接:ABC342 Problem A: 稍微有点含金量的签到题。 #include <bits/stdc.h> using namespace std; int main(){string S;cin>>S;for(int i0;i<s.size();i){if(count(S.begin(),S.end(),S[i…

多维时序 | Matlab实现基于VMD-DBO-GRU、VMD-GRU、GRU的多变量时间序列预测

多维时序 | Matlab实现基于VMD-DBO-GRU、VMD-GRU、GRU的多变量时间序列预测 目录 多维时序 | Matlab实现基于VMD-DBO-GRU、VMD-GRU、GRU的多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现基于VMD-DBO-GRU、VMD-GRU、GRU的多变量时间序列预测…

书生·浦语大模型实战营第六节课作业

基础作业 python run.py --datasets ceval_gen --hf-path /root/model/Shanghai_AI_Laboratory/internlm2-chat-7b/ --tokenizer-path /root/model/Shanghai_AI_Laboratory/internlm2-chat-7b/ --tokenizer-kwargs padding_sideleft truncationleft trust_remote_codeTrue --m…

嵌入式软件分层设计的思想分析

“嵌入式开发&#xff0c;点灯一路发” 那今天我们就以控制LED闪烁为例&#xff0c;来聊聊嵌入式软件分层: ——————————— | | | P1.1 |-----I<|--------------<| | | | P2.1 |-------------/ ---------…

Seata分布式事务实战AT模式

目录 分布式事务简介 典型的分布式事务应用场景 两阶段提交协议(2PC) 2PC存在的问题 什么是Seata&#xff1f; Seata的三大角色 Seata AT模式的设计思路 一阶段 二阶段 Seata快速开始 Seata Server&#xff08;TC&#xff09;环境搭建 db存储模式Nacos(注册&配…

HTB-Bizness

一、信息收集 访问ip自动跳转域名&#xff0c;host绑定域名后访问 目录爆破 有一个登录目录&#xff0c;访问发现是apahce ofbiz登录页面 发现存在漏洞 二、漏洞利用 在github上找到了图形化利用工具 使用工具反弹shell 得到flag 三、权限提升 从本地利用python开启http服务…

远程连接服务器及可视化方法(Win和Linux)

1.win端 1、通过SSH连接至服务器 在window下&#xff0c;打开命令行提示符&#xff08;快捷键winr后输入cmd回车&#xff09; 在命令行中输入 ssh 服务器上的用户名192.168.50.204回车并输入服务器上的用户登录密码 至此&#xff0c;已成功通过SSH连接至服务器。 2、通过…

复旦大学EMBA联合澎湃科技:共议科技迭代 创新破局

1月18日&#xff0c;由复旦大学管理学院、澎湃新闻、厦门市科学技术局联合主办&#xff0c;复旦大学EMBA项目、澎湃科技承办的“君子知道”复旦大学EMBA前沿论坛在厦门成功举办。此次论坛主题为“科技迭代 创新破局”&#xff0c;上海、厦门两地的政策研究专家、科学家、科创企…

集合的并发修改异常问题

使用迭代器遍历集合时&#xff0c;同时在删除集合中的数据&#xff0c;程序就会出现并发修改异常的错误。 import java.util.ArrayList; import java.util.Iterator; import java.util.List;public class _Exception {public static void main(String[] args) {List<String…

【GAD】DOMINANT个人解读/学习

SDM2019&#xff0c;这是一篇图异常检测领域的经典方法. 问题定义 在本文中&#xff0c;我们使用手写体来表示集合&#xff08;例如&#xff0c; V \mathcal{V} V&#xff09;&#xff0c;粗体小写字母&#xff08;例如&#xff0c; x \mathbf{x} x&#xff09;来表示向量&…

优化测试稳定性的失败重试工具:pytest-rerunfailures详解!

一.前言 笔者在执行自动化测试用例时&#xff0c;会发现有时候用例失败并非代码问题&#xff0c;而是由于服务正在发版&#xff0c;导致请求失败&#xff0c;从而降低了自动化用例的稳定性&#xff0c;最后还要花时间定位到底是自身case的原因还是业务逻辑问题&#xff0c;还是…

安全测试:史上最全的攻防渗透信息收集方法、工具!

信息收集的意义 信息收集对于渗透测试前期来说是非常重要的。正所谓&#xff0c;知己知彼百战不殆&#xff0c;信息收集是渗透测试成功的保障&#xff0c;只有我们掌握了目标网站或目标主机足够多的信息之后&#xff0c;才能更好地进行渗透测试。 信息收集的方式可以分为两种…

如何利用EXCEL批量插入图片

目录 1.excel打开目标表格&#xff1b; 2.点开视图-宏-录制宏&#xff0c;可以改宏的名字或者选择默认&#xff1b; 3.然后点开视图-宏-查看宏 4.点编辑进去 5.修改代码&#xff1a; &#xff08;1&#xff09;打开之后会显示有一堆代码 &#xff08;2&#xff09;将这个…

git之分支管理

一.理解分支 我们看下面这张图片&#xff1a; 在版本回退⾥&#xff0c;你已经知道&#xff0c;每次提交&#xff0c;Git都把它们串成⼀条时间线&#xff0c;这条时间线就可以理解为是⼀个分⽀。截⽌到⽬前&#xff0c;只有⼀条时间线&#xff0c;在Git⾥&#xff0c;这个分⽀…

SpringBootRest服务调用

目录 RestTemplate 依赖配置 自定义RestTemplate webCilent 依赖配置 自定义webCilent springboot中有两种方式实现Rest远程服务调用&#xff0c;分别是RestTemplate与webCilent。下面分别介绍一下这两种方式。 RestTemplate 依赖配置 RestTemplate是Spring Framework提供的…

使用GPT生成python图表

首先&#xff0c;生成一脚本&#xff0c;读取到所需的excel表格 import xlrddata xlrd.open_workbook(xxxx.xls) # 打开xls文件 table data.sheet_by_index(0) # 通过索引获取表格# 初始化奖项字典 awards_dict {"一等奖": 0,"二等奖": 0,"三等…

从Unity到Three.js(outline 模型描边功能)

指定模型高亮功能&#xff0c;附带设置背景颜色&#xff0c;获取随机数方法。 百度查看说是gltf格式的模型可以携带PBR材质信息&#xff0c;如果可以这样&#xff0c;那就完全可以在blender中配置好材质导出了&#xff0c;也就不需要像在unity中调整参数了。 import * as THRE…

牛客周赛 Round 34 解题报告 | 珂学家 | 构造思维 + 置换环

前言 整体评价 好绝望的牛客周赛&#xff0c;彻底暴露了CF菜菜的本质&#xff0c;F题没思路&#xff0c;G题用置换环骗了50%, 这大概是唯一的亮点了。 A. 小红的字符串生成 思路: 枚举 a,b两字符在相等情况下比较特殊 a, b input().split() if a b:print (2)print (a)pri…