【数据结构5】二叉搜索树(插入、查询、删除)

news2024/9/24 19:21:25

1 二叉搜索树
1.1 二叉搜索树-插入
1.2 二叉搜索树-查询
1.3 二叉搜索树-删除

1 二叉搜索树

二叉搜索树是一颗二叉树且满足性质:设是二叉树的一个节点。
如果y是x左子树的一个节点,那么y.key< x.key;如果y是x右子树的一个节点,那么y.key > x.key。

二叉搜索树的操作:查询、插入、删除

在这里插入图片描述

1.1 二叉搜索树-插入

class BiTreeNode:
    def __init__(self, data):
        """
        初始化二叉树节点
        :param data: 节点的值
        """
        self.data = data  # 节点的值
        self.lchild = None  # 左子节点
        self.rchild = None  # 右子节点
        self.parent = None  # 父节点


class BST:
    def __init__(self, li: list):
        """
        初始化二叉搜索树,并插入给定的值
        :param li: 包含插入值的列表
        """
        self.root = None  # 初始化根节点为空
        if li:
            for val in li:
                self.insert_no_rec(val)  # 使用非递归插入方法将列表中的值插入树中

    def insert(self, node, val):
        """
        递归插入节点到二叉搜索树中
        :param node: 当前节点
        :param val: 要插入的值
        :return: 插入后的节点
        """
        if not node:
            # 如果当前节点为空,则创建一个新节点作为叶节点
            return BiTreeNode(val)
        elif val < node.data:
            # 如果要插入的值小于当前节点的值,则递归插入到左子树
            node.lchild = self.insert(node.lchild, val)
            node.lchild.parent = node  # 更新左子节点的父节点
        elif val > node.data:
            # 如果要插入的值大于当前节点的值,则递归插入到右子树
            node.rchild = self.insert(node.rchild, val)
            node.rchild.parent = node  # 更新右子节点的父节点
        # 如果要插入的值等于当前节点的值,则不做任何操作(BST中不允许重复值)
        return node

    def insert_no_rec(self, val):
        """
        非递归插入节点到二叉搜索树中
        :param val: 要插入的值
        :return: None
        """
        p = self.root
        if not p:
            # 如果树为空,将新节点设置为根节点
            self.root = BiTreeNode(val)
            return
        while True:
            if val < p.data:
                # 如果要插入的值小于当前节点的值,则移动到左子树
                if p.lchild:
                    p = p.lchild
                else:
                    # 如果左子树为空,则在此位置插入新节点
                    p.lchild = BiTreeNode(val)
                    p.lchild.parent = p  # 更新新节点的父节点
                    return
            elif val > p.data:
                # 如果要插入的值大于当前节点的值,则移动到右子树
                if p.rchild:
                    p = p.rchild
                else:
                    # 如果右子树为空,则在此位置插入新节点
                    p.rchild = BiTreeNode(val)
                    p.rchild.parent = p  # 更新新节点的父节点
                    return
            else:
                # 如果要插入的值等于当前节点的值,则不做任何操作(BST中不允许重复值)
                return

    def pre_order(self, root):
        """
        二叉树的前序遍历
        :param root:
        :return:
        """
        if root:
            print(root.data, end=',')
            self.pre_order(root.lchild)
            self.pre_order(root.rchild)

    def mid_order(self, root):
        """
        二叉树的中序遍历
        :param root:
        :return:
        """
        if root:
            self.mid_order(root.lchild)
            print(root.data, end=',')
            self.mid_order(root.rchild)

    def post_order(self, root):
        """
        二叉树的后序遍历
        :param root:
        :return:
        """
        if root:
            self.post_order(root.lchild)
            self.post_order(root.rchild)
            print(root.data, end=',')


tree = BST([4, 5, 6, 7, 1, 3, 2, 8])
tree.post_order(tree.root)  # 2,3,1,8,7,6,5,4,
print('')
tree.mid_order(tree.root)  # 1,2,3,4,5,6,7,8,
print('')
tree.post_order(tree.root)  # 2,3,1,8,7,6,5,4,

1.2 二叉搜索树-查询

class BST:
	
    def query(self, node, val):
        """
        递归查询二叉搜索树中的节点
        :param node: 当前节点
        :param val: 要查询的值
        :return: 节点对象(如果找到)或 None(如果未找到)
        """
        if not node:
            # 如果当前节点为空,则值未找到,返回 None
            return None
        if node.data < val:
            # 如果当前节点的值小于要查询的值,则递归查询右子树
            return self.query(node.rchild, val)
        elif node.data > val:
            # 如果当前节点的值大于要查询的值,则递归查询左子树
            return self.query(node.lchild, val)
        else:
            # 如果当前节点的值等于要查询的值,返回当前节点
            return node

    def query_no_rec(self, val):
        """
        非递归查询二叉搜索树中的节点
        :param val: 要查询的值
        :return: 节点对象(如果找到)或 None(如果未找到)
        """
        p = self.root
        while p:
            if p.data < val:
                # 如果当前节点的值小于要查询的值,则移动到右子树
                p = p.rchild
            elif p.data > val:
                # 如果当前节点的值大于要查询的值,则移动到左子树
                p = p.lchild
            else:
                # 如果当前节点的值等于要查询的值,返回当前节点
                return p
        # 如果遍历结束仍未找到值,返回 None
        return None

1.3 二叉搜索树-删除

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class BST:
    # 其他方法...

    def __remove_node_1(self, node):
        """
        情况1:node是叶子节点
        :param node: 要删除的节点
        :return: None
        """
        if not node.parent:
            # 如果节点没有父节点,说明它是根节点,直接将根节点置为 None
            self.root = None
        elif node == node.parent.lchild:
            # 如果节点是它父节点的左孩子,则将父节点的左孩子指针置为 None
            node.parent.lchild = None
        else:
            # 如果节点是它父节点的右孩子,则将父节点的右孩子指针置为 None
            node.parent.rchild = None
        node.parent = None  # 清除删除节点的父节点引用

    def __remove_node_21(self, node):
        """
        情况2.1:node只有一个左孩子
        :param node: 要删除的节点
        :return: None
        """
        if not node.parent:
            # 如果节点没有父节点,说明它是根节点,将根节点指向它的左孩子
            self.root = node.lchild
            node.lchild.parent = None
        elif node == node.parent.lchild:
            # 如果节点是它父节点的左孩子,则将父节点的左孩子指向该节点的左孩子
            node.parent.lchild = node.lchild
            node.lchild.parent = node.parent
        else:
            # 如果节点是它父节点的右孩子,则将父节点的右孩子指向该节点的左孩子
            node.parent.rchild = node.lchild
            node.lchild.parent = node.parent

    def __remove_node_22(self, node):
        """
        情况2.2:node只有一个右孩子
        :param node: 要删除的节点
        :return: None
        """
        if not node.parent:
            # 如果节点没有父节点,说明它是根节点,将根节点指向它的右孩子
            self.root = node.rchild
        elif node == node.parent.lchild:
            # 如果节点是它父节点的左孩子,则将父节点的左孩子指向该节点的右孩子
            node.parent.lchild = node.rchild
            node.rchild.parent = node.parent
        else:
            # 如果节点是它父节点的右孩子,则将父节点的右孩子指向该节点的右孩子
            node.parent.rchild = node.rchild
            node.rchild.parent = node.parent

    def delete(self, val):
        """
        删除二叉搜索树中的节点
        :param val: 要删除的节点的值
        :return: 如果删除成功返回 True,否则返回 False
        """
        if self.root:  # 如果树非空
            node = self.query_no_rec(val)  # 查找要删除的节点
            if not node:
                return False  # 如果节点不存在,返回 False
            if not node.lchild and not node.rchild:
                # 情况1:叶子节点
                self.__remove_node_1(node)
            elif not node.rchild:
                # 情况2.1:只有左孩子
                self.__remove_node_21(node)
            elif not node.lchild:
                # 情况2.2:只有右孩子
                self.__remove_node_22(node)
            else:
                # 情况3:左右孩子都有
                # 查找右子树中最小的节点
                min_node = node.rchild
                while min_node.lchild:
                    min_node = min_node.lchild
                node.data = min_node.data  # 用最小节点的值替换当前节点的值
                # 删除最小节点
                if min_node.rchild:
                    self.__remove_node_22(min_node)
                else:
                    self.__remove_node_1(min_node)
            return True  # 删除成功
        return False  # 树为空,删除失败

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

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

相关文章

【非常困难】 猿人学web第一届 第10题 js 混淆 - 重放攻击对抗

文章目录 调试干扰第一个 debuggerHook 代码 第二个 debuggerHook 代码 第三个 Function.constructor.prototype 检测Hook 代码 完整的 Hook 代码 接口分析加密参数 m 生成位置加密参数还原加密点扣代码提示_yrxyA\$()var _yrxmEu _yrxWKg(_yrxyHJ...._yrxWKg() let result _…

命令执行漏洞-rce

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 1.1 命令执行漏…

47.x86游戏实战-VEHHOOK封包函数

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

Aiseesoft Mac Video Converter Ultimate:高效多能的视频转换与编辑工具

Aiseesoft Mac Video Converter Ultimate是一款视频转换与编辑软件&#xff0c;以其强大的功能和简单易用的操作界面赢得了广泛好评。该软件不仅支持多种视频格式的转换&#xff0c;还集成了丰富的视频编辑功能&#xff0c;满足了用户多样化的视频处理需求。 核心功能概述 视频…

mysqlcheck

mysql bin中的其他工具包 对于mysql的其他工具, 有很多选项是公共的, 例如你在对数据库进行检查的时候, 需要指定host和user以及其password来连接上mysqlserver来进行相关操作, 这个时候其实用到的host等东西跟我们之前的mysql-client其实是类似的 也就是说他们是作为mysql众…

顺序表的顺序表示—动态分配

顺序表的顺序表示—动态分配 代码实现 #include <stdio.h> #include <stdlib.h> #define InitSize 15 // 初始化扩容长度typedef struct{int *data; // 动态分配数组的指针int MaxSize;int length; // 当前长度 }SeqList;void InitList(SeqList &L){// 申请一…

C++不同数据类型连接成一个字符串

在C中数据连接的方式使用号进行连接。 1.都是字符型时直接使用连接几个字符串&#xff1b; 2.不是字符类型时&#xff0c;要用to_string函数转换后再连接。

做空股指期货一手多少钱?

股指期货的保证金比例是12%-15%不等&#xff0c;所以做空一手股指期货的保证金最少是要十几万元&#xff0c;部分平台两万。关于做空一手股指期货的具体金额&#xff0c;这并非固定不变&#xff0c;而是会根据市场的实时价格、合约的乘数以及交易所的规定等因素而有所变动。 股…

Verilog刷题笔记59

题目: Exams/m2014 q6c 解题&#xff1a; module top_module (input [6:1] y,input w,output Y2,output Y4);assign Y2y[1]&w0;assign Y4(y[2]&w1)|(y[3]&w1)|(y[5]&w1)|(y[6]&w1);endmodule结果正确: 注意点: 起初&#xff0c;我的代码有错误,代码如下…

快速判断一个项目是Spring MVC框架还是Spring Boot框架

1. 查看项目的启动类 Spring Boot: 通常有一个主类&#xff0c;包含 SpringBootApplication 注解&#xff0c;并且有一个 main 方法来启动应用程序。 SpringBootApplication public class Application {public static void main(String[] args) {SpringApplication.run(Appli…

探索数据结构:图(三)之最短路径算法

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 最短路径算法 最短路径问题可分为单源最短路径和多源最短路径。其指…

如何使用ssm实现珠宝首饰交易平台开发

TOC ssm101珠宝首饰交易平台开发jsp 第一章 绪 论 1.1背景及意义 系统管理也都将通过计算机进行整体智能化操作&#xff0c;对于珠宝首饰交易平台所牵扯的管理及数据保存都是非常多的&#xff0c;例如管理员&#xff1b;主页、个人中心、用户管理、商品分类管理、商品信息管…

【Material-UI】Radio Group中的 Label Placement 属性详解

文章目录 一、Radio Group 组件概述1. 组件介绍2. Label Placement 属性的作用 二、Label Placement 属性的基本用法三、Label Placement 属性详解1. 标签位置的选择2. 如何在实际项目中选择标签位置 四、Label Placement 属性的实际应用场景1. 表单布局中的应用2. 符合用户习惯…

多进程多线程及之间通信机制

目录 前言 一、多进程与多线程 1. 多进程 多进程的特点 多进程的应用场景 2. 多线程 多线程的特点 多线程的应用场景 3. 多进程与多线程的对比 二、进程与线程之间的通信机制 1. 进程间通信&#xff08;IPC, Inter-Process Communication&#xff09; 2. 线程间通信…

Golang | Leetcode Golang题解之第365题水壶问题

题目&#xff1a; 题解&#xff1a; type pair struct {x, y int }func canMeasureWater(jug1Capacity int, jug2Capacity int, targetCapacity int) bool {//剪枝if jug1Capacityjug2Capacity < targetCapacity {return false}var (dfs func(x, y int) bool // jug1有x水…

NC包含min函数的栈

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 定义栈的数据…

基于STM32开发的智能水箱液位控制系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化液位监测与控制水泵控制与状态显示Wi-Fi通信与远程监控应用场景 家庭用水系统的液位控制工业水箱的液位管理常见问题及解决方案 常见问题解决方案结论 1. 引言 智能水箱液位控制系…

线程池详解(建议收藏)

概念 线程池&#xff08;Thread Pool&#xff09;是一种基于池化技术的多线程处理形式&#xff0c;用于管理线程的创建和生命周期&#xff0c;以及提供一个用于并行执行任务的线程队列。线程池的主要目的是减少在创建和销毁线程时所花费的开销和资源&#xff0c;提高程序性能&…

C# messagePack对类(class)序列化简单示例

c# messagepack对类&#xff08;class&#xff09;序列化的简单示例 0 引言1 示例2 纠错3 结语 0 引言 MessagePack是一种高效的二进制序列化格式&#xff0c;它可以在C#中用于序列化和反序列化对象。与其他序列化格式相比&#xff0c;如JSON和XML&#xff0c;MessagePack的编…

西安电子高速PCB学习(五)

感抗&#xff08;Inductive Reactance&#xff09;和容抗&#xff08;Capacitive Reactance&#xff09;是电感和电容在交流电路中对电流产生阻碍的特性。这两个概念源于交流电路中&#xff0c;电感和电容对交流电流的相应反应。 感抗&#xff08;Inductive Reactance&#xf…