植物大战 二叉搜索树——C++

news2025/1/11 19:44:09

这里是目录标题

  • 二叉排序树的概念
  • 模拟二叉搜索树
    • 定义节点类
    • insert非递归
    • Find
    • erase(重点)
  • 析构函数
  • 拷贝构造(深拷贝)
  • 赋值构造
  • 递归
    • FindR
    • InsertR
  • 二叉搜索树的应用
    • k模型
    • KV模型

二叉排序树的概念

单纯的二叉树存储数据没有太大的作用。

搜索二叉树作用很大。

搜索二叉树的一般都是用来查找。也可以用来排序,所以也叫做二叉排序树
叫做二叉排序树的原因是因为他中序遍历是有序的。
因为中序的任何一颗子树,都要先走左子树,根,再走右子树。

搜索树因为数据不能冗余,所以也可以用来在插入的时候去重

因为左子树<根<右子树,所以走中序出来,就是排序好的结果。

搜索二叉树要满足:
任意一颗子树都要满足,左子树的值小于根,右子树的值大于根。

优点:查找速度非常快。查找一个值,最多查找高度次,但是它的查找速度是O(n)。因为它有可能是单边树。所以后面会有一个平衡搜索二叉树,不如AVL树,红黑树。

但是我们要从搜索二叉树学起。

模拟二叉搜索树

搜索树的模板参数喜欢用k。k表示关键词的意思,因为喜欢搜索。

定义节点类

需要先定一个节点类。

template<class K>
struct BSTNode
{

	BSTNode<K>* _pLeft;
	BSTNode<K>* _pRight;

	K _key;
};

然后开始写二叉树的增删查改。

insert非递归

原则上插入不能有相同的数据插入。

搜索二叉树的插入顺序会影响效率。一般有序的话会影响。
为了能够找到cur的上一个结点,所以需要再定义个parent的节点。

template <class K>
class BSTree
{
	typedef BSTreeNode Node;//名字优化
public:
	bool Insert(const K& key)
	{
		//如果根节点为空
		if (_root == nullptr)
		{
			//new的时候直接可以填值。
			_root = new Node(key);

			return bool;
		}
		//否则开始
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			//如果在左边
			if (key > cur->_key)
			{
				parent = cur;
				cur = cur->_right;

			}
			//如果在右边
			else if(key < cur->_key)
			{
				parent = cur;
				cur = cur->_left;
			}
			//不允许数据冗余
			else
			{
				return false;
			}
		}
		//循环结束,new一个空间.
		cur = new Node(key);
		//不知道链接到父亲的左边和右边。
		//这时候就需要再比较一次
		if (key > parent->_key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		return true;
	}
private:
	Node* _root = nullptr;//根节点
};

C++的根节点是私有的,封装了。所以没办法访问。
可以提供函数。也可以套一层。用_InOrder。
然后把_InOrder函数设为私有。

这样就不用传参了。
在这里插入图片描述

Find

查找值更简单。

   bool Find(const K& key)
    {
        Node* cur = _root;
        while(cur)
        {
            if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}

        }

        return false;
    }

erase(重点)

搜索树前面的问题都不算问题,最大的问题在于删除数据。删除数据是一个非常麻烦的事情。

1.删叶子节点最好删。
2.删只有一个孩子的也挺好删。只有一个孩子的话,托付给父亲就行,和Linux的托孤有点相似。

总结:没有孩子或者只有一个孩子的时候可以直接删除。
3.不好删的是:
假如有两个孩子则不好删。
在这里插入图片描述

比如删除3。3有两个孩子。3的父亲8管不了两个孩子,因为8右子树也有孩子。

这时候需要用替换法删除。
替换法删除

找谁替换呢?
找左子树的最大值结点。或者右子树的最小值结点。
为什么?
因为一棵搜索二叉树,最左边就是最小的值。最右边是最大的值。

关键理解左子树的最大值结点做父亲可以满足比左子树的任意一个结点的值都大,同时随便拿出左子树的任意一个结点都比右子树小。

理解了上面的。有人找出了规律。
重点
分三种情况
1.假如删除的节点的左子树为空(包括两个节点都为空的情况)。
2.假如删除的节点的右子树为空。(要注意假如是根节点的情况)
3.假如删除的节点的左右子树都不为空(替换法删除)。

具体遇到的bug需要根据实际图来解决。上面的三种情况只是个大概。

//非递归删除erase
	   bool Erase(const K& key)
    {
        Node* parent = nullptr;
        Node* cur = _root;
        while(cur)
        {
            if(key > cur->_key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if(key < cur->_key)  
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                //一个孩子 假如 左为空
                if(cur->_left == nullptr)
                {
                    if(cur == _root)
                    {
                        _root = cur->_right;
                    }

                    else{
                        if(cur == parent->_left)
                        {
                            parent->_left = cur->_right;
                        }
                        else
                        {
                            parent->_right = cur->_right;
                        }
                    }
                    delete cur;
                }
                //假如右为空
                else if(cur->_right == nullptr)
                {
                    if(cur == _root)
                    {
                        _root = cur->_left;
                    }

                    else{
                        if(cur == parent->_left)
                        {
                            parent->_left =  cur->_left;
                        }
                        else{
                            parent->_right = cur->_left;
                        }
                    }

                    delete cur;
                }

                //两个孩子都不为空
                else
                {
                    //右子树最小节点替代
                    Node* minParent = cur;
                    Node* minRight = cur->_right;
                    while(minRight->_left)
                    {
                        minParent = minRight;
                        minRight = minRight->_left;
                    }

                    swap(minRight->_key, cur->_key);

                    if(minParent->_left == minRight)
                    {
                        minParent->_left = 	minRight->_right;
                    }
                    else
                    {
                        minParent->_right = minRight->_right;
                    }

                    delete minRight;
                }

                return true;
            }
        }

        return false;
    }

析构函数

因为没有参数,无法调用析构函数递归,所以需要套一层进行递归。

private:
void DestortTree(Node* root)
{
	if(root == nullptr)
	return;
	DestoryTree(root->_left);
	DestoryTree(root->_right);
	delete root;
}
public:
~BSTree()
{
	DestoryTree(_root);
	_root = nullptr;
}

拷贝构造(深拷贝)

注意:只要有了拷贝构造就不会再生成默认的构造函数了,所以为了写拷贝构造函数,需要先写一个构造函数。
这时候就要显式写一个构造函数。或者可以用C++11的关键字default来强制生成默认构造

BSTree() = default;

假如我们不写拷贝构造函数,会默认生成构造函数进行浅拷贝。
为了保证树的形状,只能用前序遍历(根 左子树 右子树)递归拷贝

private:
Node* CopyTree(Node* root)
{
	if(root == nullptr)
		return nullptr;
		
	Node* copyNode = new Node(root->key);
	copyNode->_left = CopyTree(root->_left);
	copyNode->_right = CopyTree(root->_right);
	
	return copyNode;
}
public:
BSTree(const BSTree<K>&  t)
{
	_root = CopyTree(t._root);
}

赋值构造

// t1 = t2

BSTree<K>& operator=(BSTree<K> t)
{
	swap(_root, t._root);
	return *this;
}

递归

FindR

返回下表的原因是因为要修改,但是这个树不需要修改,修改会破坏掉结构。所以返回bool值就ok。

C++类只要走递归一般都要套一层。不套一层一般都无法走递归。

InsertR

    bool InsertR(const K& key)
    {
        return _InsertR(_root, key);
    }

 bool _InsertR(Node*& root, const K& key)
    {
        if(root == nullptr)
        {
            root = new Node(key);
            return true;
        }

        if(root->_key < key)
            return _InsertR(root->_right, key);
        else if(root->_key > key)
            return _InsertR(root->_left, key);
        else
            return false;
    }

二叉搜索树的应用

k模型

k模型只有key作为关键码,结构中只需要存储key杰克,关键码即为需要搜索到的值,比如给一个单词word,检查该单词是否拼写正确。

实质:就是判断K在不在这个系统中

K模型也可以用来去重+排序,

KV模型

KV模型的每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。

1、比如英汉词典中的英文和中文的对应关系,构成了<word,chinese> 的键值对

2、统计单词出现的次数,<word, count>的关系就是一个键值对。

再比如高铁刷身份证进站。

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

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

相关文章

摸鱼用python获取弹幕的两种方式【前者简单,后者数据好看】

嗨害大家好鸭&#xff01;我是小熊猫~ 相信大家对于 “弹幕文化” 已经相当熟悉啦 你不是一个人在看——这就是弹幕网站的存在感。 它形成了新的“抱团”观看模式&#xff0c; 也真正实现了无时空距离的社交。 有网友表示&#xff0c;弹幕简直比剧情还有趣。 看似简单的寥寥…

【ES】Elasticsearch-深入理解索引原理

文章目录Elasticsearch-深入理解索引原理读操作更新操作SHARD不变性动态更新索引删除和更新实时索引更新持久化Segment合并近实时搜索&#xff0c;段数据刷新&#xff0c;数据可见性更新和事务日志更新索引并且将改动提交修改Searcher对象默认的更新时间Elasticsearch-深入理解…

CentOS8基础篇9:进程的延迟与周期调度

一、进程的概念 进程&#xff1a;开始执行但是还没有结束的程序的实例 程序&#xff1a;包含可执行代码的文件 进程与程序的关系 进程由程序产生&#xff0c;是一个运行着的、要占系统资源的程序 进程不等于程序 浏览网络时&#xff0c;打开多个IE浏览器程序&#xff1b;…

一文讲清楚如何进行主数据编码

主数据编码作为一类重要的数据资源&#xff0c;在信息化建设中具有重要的地位和作用&#xff0c;是保证现有信息系统和未来新系统建设成功的关键因素&#xff0c;决定着系统中的信息一致性。 编码&#xff0c;是一件简单的事情&#xff0c;但绝对不是一件容易做好的事情&#…

FPGA案例开发手册——基于全志T3+Logos FPGA核心板

前 言 本文档主要提供评估板FPGA端案例测试方法,适用的开发环境为Windows 7 64bit和Windows 10 64bit。 本文案例基于创龙科技的全志T3+Logos FPGA核心板,它是一款基于全志科技T3四核ARM Cortex-A7处理器 + 紫光同创Logos PGL25G/PGL50G FPGA设计的异构多核全国产工业核心板…

PImpl(Pointer to Implementation)指向实现的指针 [使用ChatGPT学习系列]

PImpl是Pointer to Implementation的缩写&#xff0c;也被称为“编译期实现”&#xff0c;是一种C设计的模式。 用于将类的实现细节与其公共接口分离开来。该模式的核心思想是 通过一个指向类的实现的指针来隐藏类的实现细节&#xff0c;从而提高类的封装性和安全性。PImpl是一…

「考研算法」

考研算法 前言 本系列文章涉及的算法内容&#xff0c;针对的是哈尔滨工业大学854科目。在本文中通过具体的算法题进行讲解相应算法。 今天涉及的算法主要有线性筛&#xff0c;十大排序中快速排序和归并排序。 后续会有动态规划的相关算法以及尝试模型的总结&#xff0c;如果…

[Java·算法·中等]LeetCode17. 电话号码的字母组合

每天一题&#xff0c;防止痴呆题目示例分析思路1题解1分析思路2题解2题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。…

本科毕业设计-基于ORB SLAM3的多从机SLAM导航系统

耗时&#xff1a;两个月 需求&#xff1a;多从机协作 多地图系统 稠密建图 定位 导航 硬件&#xff1a;二个D435 一台X86主机&#xff08;CPU:13600kf 内存:32G&#xff09; X86主机环境&#xff1a;ubuntu18.04 opencv3.2 ROS1 主要代码参考&#xff1a;ORB-SLAM3 主要调用…

【CNN】FractalNet——与DenseNet有异曲同工之妙

FractalNet论文名称&#xff1a;FractalNet: Ultra-Deep Neural Networks without Residuals FractalNet论文下载链接&#xff1a; FractalNet&#xff08;分型网络&#xff09;&#xff0c;2016年Gustav Larsson首次提出。 &#xff08;1&#xff09;分形网络不像resNet那样…

嵌入式 Linux 文件IO操作

目录 Linux 文件操作 1 Linux 系统环境文件操作概念 2 缓冲 IO 文件操作 1 文件的创建&#xff0c;打开与关闭 fopen 函数函数 2 freopen 函数 3、fdopen函数 4、fclose函数 5、格式化读写 6、单个字符读写 7、文件定位 8、标准目录文件 9、非缓冲IO文件操作 Linux 文…

十二、MyBatis的高级映射及延迟加载

1 数据库表的准备 准备数据库表&#xff1a;一个班级对应多个学生。班级表&#xff1a;t_clazz。学生表&#xff1a;t_stu 2 环境搭建 创建模块 打包方式&#xff1a;jar 引入依赖&#xff1a;mybatis依赖、mysql驱动依赖、junit依赖、logback依赖 配置文件&#xff1a;…

C#/.net程序调用python

C#/.net程序调用python C#的优势在于window下的开发&#xff0c;不仅功能强大而且开发周期短。而python则有众多的第三方库&#xff0c;可以避免自己造轮子&#xff0c;利用C#来做界面&#xff0c;而具体实现使用python来实现可以大大提高开发效率。本文介绍如何使用pythonnet…

Kubernetes初始化容器

初始化容器 之前了解了容器的健康检查的两个探针&#xff1a;liveness probe&#xff08;存活探针&#xff09;和readiness probe&#xff08;可读性探针&#xff09;的使用方法&#xff0c;我们说在这两个探针是可以影响容器的生命周期的&#xff0c;包括我们之前提到的容器的…

如何或者无插件Web页面监控播放软件LiveNVR的固定视频流地址,实现大屏上墙、播放、视频分析等目的

1、LiveNVR介绍 LiveNVR的安防监控的视频直播&#xff0c;可以按标准的Onvif/RTSP协议接入监控设备&#xff0c;也可以通过海康、大华、天地伟业等厂家私有SDK接入监控&#xff0c;实现web页面的播放和录像回放。 可以分发HTTP-FLV、WS-FLV、WebRTC、RTMP、HLS(M3U8)、RTSP等多…

Linux安装Tomcat9

默认Linux已经安装了JDK 并且已经配置好了环境变量 下载链接 Tomcat9 下载完成如下图 &#xff0c;这个下载完成需要看一下&#xff0c;有的包里bin目录内缺少bootstrap.jar文件&#xff0c;因此下载包的时候要看看bin目录下的是不是有这个文件&#xff0c;如果没有启动Tomcat…

CHAPTER 1 Linux 集群

集群1 集群介绍2 集群分类1. 高可用性集群&#xff08;High Availability Cluster&#xff09;HA2. 负载均衡集群&#xff08;Load Balance Cluster&#xff09;LB3. 高性能集群&#xff08;High Performance Computing Cluster&#xff09;HPC3 HA集群逻辑架构1. 信息层/基础架…

Qt页面菜单栏、工具栏、状态栏

1、菜单栏 QMenu *editMenu ui->menuBar->addMenu("编辑(&E)");2、编辑菜单栏及工具栏内容 QAction *action_copy editMenu->addAction(QIcon("copy.png"),QString("复制(&c)"));action_copy->setShortcut(QKeySequence(…

数学建模竞赛的一些心得体会

1.数学建模经验首先简要的介绍一下我的情况。数学建模我也是在大一暑假开始接触的&#xff0c;之前对其没有任何的了解。我本身对数学也有相对较厚的兴趣&#xff0c;同时我也是计算机专业的学生&#xff0c;因此&#xff0c;我觉得我可参加数学建模的这个比赛。大一的暑假参加…

Linux->进程终止和等待

目录 1. 进程终止场景 1.1 进程退出码 1.2 进程常见退出方式 2. 进程等待 2.1 进程等待的必要性 2.2 进程等待的方式 wait()方式 waitpid()方式 options参数 status参数 1. 进程终止场景 代码运行完毕&#xff0c;结果正确 代码运行完毕&#xff0c;结果不正确 代码异…