二叉搜索树+二叉进阶oj

news2024/11/13 10:50:46

索引

  • 二叉搜索树
    • 1.二叉搜索树概念与性能分析
    • 2.二叉树的应用
    • 3.二叉树的实现细节
    • 4.类中函数递归调用
    • 5.二叉树实现总体代码
    • 6.二叉进阶oj
    • 7.二叉树三种遍历非递归实现

二叉搜索树

1.二叉搜索树概念与性能分析

什么是二叉搜索树
二叉搜索树也被称为二叉排序树,其也可以是一棵空树,若左子树不为空,那么左子树上所有节点值都小于根节点的值,若右子树不为空,那么右子树上的所有结点值都大于根节点且其左右子树的都是二叉搜索树。所以可二叉搜索树是不允许出现元素重复的
eg:
​​​​​​​​​​在这里插入图片描述
二叉树性质分析与应用
如上图所示可以看出,二叉树在查找时,最好的情况:
二叉树是完全二叉树或者接近完全二叉树,此时时间复杂度是Olog2(N),但如果二叉树是单支二叉树或者类似单支,比较的次数就是数高度也就是数的个数,所以此时的时间复杂度是O(N);
在这里插入图片描述

2.二叉树的应用

1.K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值
2.KV模型:每一个关键码Key,都有与之对应的值Value,即<Key,Value>的键值对,该种方式在实际生活中很常见,eg:英汉字典<word,chinese> 统计人的身高<people,height>

3.二叉树的实现细节

二叉树的插入:
1.树为空,此时直接new一个新结点然后赋值给root
2.树不为空,按照上述查找的规则,查找插入的位置,插入新节点
二叉树的删除
二叉树的删除比较复杂,首先需要查找元素是否在二叉搜索树中,如果不存在,那么直接返回false,即使存在,需要删除的结点也要分下面四种情况:
a.删除结点没有孩子
b.要删除的结点只有左孩子
c.要删除的结点只有右孩子
d.要删除的结点既有左孩子,也有有孩子
前面三种比较简单,但是第四种需要详细的考虑一下。
注: 删除的结点有可能就是头结点,这个无论在哪种情况下都得拿出来单独分析。
同时情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用其值与被删除结点交换,再来处理该节点的删除问题。

bool Erase(const K& key)
	{
		Node* cur = _root;
		Node* curParent = nullptr;
		while (cur)
		{
			if (cur->_key < key)
			{
				curParent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				curParent = cur;
				cur = cur->_left;
			}
			else
			{
				//此时cur->_key = key
			 //分情况讨论 
			 //其没有左子树
				if (cur->_left == nullptr)
				{
					//有可能此时cur就是头节点
					//此时curParent还是nullptr
					//所以要做特判
					if (curParent == nullptr)
					{
						_root = cur->_right;
						delete cur;
					}
					else
					{
						if (cur == curParent->_left)
						{
							curParent->_left = cur->_right;
						}
						else
						{
							curParent->_right = cur->_right;
						}
						delete cur;

					}
					return true;
				}
				else if (cur->_right == nullptr)
				{
					if (curParent == nullptr)
					{
						_root = cur->_left;
						delete cur;
					}
					else
					{
						if (cur == curParent->_left)
						{
							curParent->_left = cur->_left;
						}
						else
						{
							curParent->_right = cur->_left;
						}
						delete cur;

					}
					return true;
				}
				//左右孩子都不为空
				else
				{
					Node* min_Right_Parent = cur;

					Node* min_Right = cur->_right;
					while (min_Right->_left)
					{
						min_Right_Parent = min_Right;
						min_Right = min_Right->_left;
					}
					swap(min_Right->_key, cur->_key);
					//有可能上述中循环压根不会进入
					if (min_Right == cur->_right)
					{
						min_Right_Parent->_right = min_Right->_right;
					}
					else
					{
						min_Right_Parent->_left = min_Right->_right;

					}
					delete min_Right;

					return true;
				}
			}

		}
		return false;

	}

4.类中函数递归调用

在二叉树中,因为很容易转换成子问题,并且到叶子结点的时候往往就是截至条件,所以无论是上述的插入还是删除都可以采用递归。递归的缺陷就是如果树很深的话,递归层数太多,有可能会栈溢出。
由于递归需要传结点,指针,但是在类中我们只有一个结点就是根节点,所以我们要采用一种封装的思想。

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

	bool _InsertR(Node*& root, const K& key, const V& value)
	{
		if (root == nullptr)
		{
			root = new Node(key, value);
			return true;
		}
		if (root->_key < key)
			return _InsertR(root->_right, key, value);
		else if (root->_key > key)
			return _InsertR(root->_left, key, value);
		else
			return false;

	}

注意:此时递归参事指针时候,指针必须引用传参,因为如果不传引用的话,root就是一个局部变量,出了作用域就会销毁,无法和其父节点连接,此时可以采取再添加一个父节点参数,或者添加引用
再比如:
析构函数不能直接调用递归,因为类中的析构函数没有参数,所以可以用析构函数调用另外一个递归函数,并且再调用另外一个递归函数时,给其传进去的参数一定是成员变量。

	~BSTree()
	{
		_DestoryTree(_root);
		_root = nullptr;
	}
	void _DestoryTree(Node* root)
	{
		if (root == nullptr)
			return;
		_DestoryTree(root->_left);
		_DestoryTree(root->_right);
		delete(root);

	}

5.二叉树实现总体代码

(函数后面有R表示递归函数,没R表示普通函数)
二叉树实现总体代码

6.二叉进阶oj

606. 根据二叉树创建字符串
关键::如果左子树为空,但是右子树不为空str+“()”
同时左右子树都得用()括号括起来
102. 二叉树的层序遍历
**关键:**层序遍历一定要用队列,头结点出来队列时,其左右孩子入队列
236. 二叉树的最近公共祖先
关键寻找p,q结点,开始根节点都是root,如果最初p或q有一个等于root的话,那么此时root就是他们的最近公共祖先。如果p和q分别在root不同的子树,那么此时的root也是最近公共祖先。
优化做法可以找出p,q到到根节点的路径,将路径上的结点存到栈上。类似于单链表找两个链表的公共结点
JZ36 二叉搜索树与双向链表
关键线索化,中序遍历,用cur与pre遍历完整个二叉树注意,起初的pre是nullptr,最后cur到达nullptr的时候遍历结束。注意:参数cur不能是引用传参,其每个结点地址只需要拷贝即但是pre自始自终都是同一个,所以得引用传参

根据一棵树的前序遍历与中序遍历构造二叉树
关键先构造左右子树,再构造根节点,前序遍历理论上而言每个结点都是某个子树的根节点,中序遍历可以确定左右子树的区间范围,root->left = 函数递归(),root->right = 函数递归();

oj代码实现

7.二叉树三种遍历非递归实现

在这里插入图片描述
前序遍历:根 左 右

 vector<int> preorderTraversal(TreeNode* root) {
      
       stack<TreeNode*>st;
       vector<int>vt;
       TreeNode*cur = root;
       while(cur||!st.empty())
       {
            while(cur)
            {
                 st.push(cur);
                 vt.push_back(cur->val);
                 cur = cur->left;

            }
            auto top =st.top();
            st.pop();
            cur = top->right;


       }
       return vt;

    }

直接将每个结点的左路结点走完入栈,然后找此时栈顶的非空右子树,按照之前一样走完左路结点入栈即可
中序遍历: 左 根 右
**关键:**左路结点入栈,一个结点从出栈时,它的左子树就访问完了,所以此时访问他以及他的右子树,本质差不多,只是数组的插入时间不同

 vector<int> inorderTraversal(TreeNode* root) {
        
         
       stack<TreeNode*>st;
       vector<int>vt;
       TreeNode*cur = root;
       while(cur||!st.empty())
       {
            while(cur)
            {
                 st.push(cur);
                 cur = cur->left;

            }
            auto top =st.top();
            st.pop();
            //从栈里面拿出来,说明此时其左子树已经访问完了
            //此时便继续访问其根节点和右子树
                 vt.push_back(top->val);

            cur = top->right;


       }
       return vt;
    }

后序遍历:左右跟
关键 每个结点理论上来说都是根节点,访问根节点要满足两种条件任意一种,如果根节点的右子树为nullptr,又或者是此时成功访问的上一个结点时根节点的右儿子,那么此时就可以访问根节点,否则不行。

  vector<int> postorderTraversal(TreeNode* root) {
         vector<int>v;
         stack<TreeNode*>st;
         TreeNode*cur = root;
         TreeNode*prev = nullptr;
         while(cur||!st.empty())
         {
             while(cur)
             {
                 st.push(cur);
                 cur = cur->left;
             
             }
           TreeNode*top = st.top();
           //如果其没有右子树此时可以出栈 并且push_back
           //如何判定根节点的右子树已经进入了
           //如果上一个进入的节点是根节点的right那么说明此时
           //该根节点左右子树都已访问完毕,便可以继续访问根节点
           if(top->right==nullptr||top->right==prev)
           {
                v.push_back(top->val);
                st.pop();
                prev = top;

           }
           else
           {
               cur = top->right;
           }
  


         }
         return v;
    }

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

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

相关文章

热视觉2:使用Python和OpenCV从图像中测量温度

热视觉2:使用Python和OpenCV从图像中测量温度 这篇博客将介绍学习热/中远红外视觉的基础知识,gray8 和 gray16 图像(即最常见的热成像格式)之间的差异,学习如何使用Python和OpenCV从热图像中的每个像素点测量温度,并以不同的调色板显示结果。以及如何实时计算视频流和UV…

2022高频经典前端面试题(vue2+vue3+typescript+nodejs下篇,含答案)

vue2篇 1.vue的生命周期&#xff08;重点&#xff09; 2.父子组件的钩子函数顺序 页面初次渲染的时候&#xff1a; 父beforeCreate-> 父created -> 父beforeMount 子beforeCreate-> 子created ->子beforeMount-> 子mounted 父mounted 页面更新的时候&#xff…

大数据之Hive

文章目录前言一、数据仓库介绍&#xff08;一&#xff09; 数据仓库的基本概念&#xff08;二&#xff09;数据仓库的主要特征&#xff08;三&#xff09;数据仓库与数据库的区别二、Hive的概念&#xff08;一&#xff09;Hive的介绍&#xff08;二&#xff09;Hive的架构总结前…

4种方法!怎么把电脑上的音频传到苹果手机上?

案例分享 我新买了iphone13&#xff0c;想把macbook上下载的音乐传输一份到手机上&#xff0c;怎么把电脑上的音频传到苹果手机上&#xff1f;能否通过网络实现将电脑音乐传输到手机播放&#xff1f;” 很多果粉在刚拿到心爱的苹果手机后&#xff0c;都先把之前收藏的歌曲传到…

Grpc简介

博主在5月份已经从上一家公司辞职了&#xff0c;七月份已经到现在的公司了&#xff0c;能在整个互联网大环境都不好的前提下&#xff0c;还能找到工作&#xff0c;待遇还不错&#xff0c;已经很知足了。grpc是进到公司的第一个项目用到的技术&#xff0c;已经用了很久也总结了&…

三菱FX5U系列PLC内置高速计数器的使用方法示例

三菱FX5U系列PLC内置高速计数器的使用方法示例 三菱FX5U系列PLC内置高速计数器如何使用? 具体的使用方法可参考如下: 如下图所示,打开GX-WORKS3编程软件,在左侧的项目树中找到参数—FX5U—模块参数—高速I/O, 如下图所示,在弹出的右侧窗口中双击高速计数器—详细设置,…

没有十几年的积累,你还真写不出什么好的代码

如标题所说&#xff0c;我不知道大家怎么看待这句话&#xff1f; 拿一个正常的程序员举个例子&#xff0c;18开始上大学学习写代码&#xff0c;22岁大学毕业&#xff0c;一直干到30岁就需要考虑程序员的中年危机。 小编身边很多程序员都不喜欢写代码&#xff0c;感觉写代码没有…

C · 进阶 | 指针的进阶

啊我摔倒了..有没有人扶我起来学习.... &#x1f471;个人主页&#xff1a;《CGod的个人主页》\color{Darkorange}{《CGod的个人主页》}《CGod的个人主页》交个朋友叭~ &#x1f492;个人社区&#xff1a;《编程成神技术交流社区》\color{Darkorange}{《编程成神技术交流社区》…

数电学习(六、时序逻辑电路)(一)

文章目录引言概述特点时序电路的一般结构形式与功能描述方法时序电路分类时序电路的分析方法同步时序电路的分析方法状态转换表状态转换图&#xff08;回顾&#xff09;在现在的场景下看触发器的动态特性&#xff08;四个时间&#xff09;&#xff08;举例&#xff09;分析下面…

计算机毕设(附源码)JAVA-SSM佳音大学志愿填报系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

python有哪些编译器

python有哪些编译器 1、Brython把Python转换成Javascript代码。 是一个流行的Python编译器&#xff0c;它可以把Python转换成Javascript代码。该软件支持所有网络浏览器(包括手机网络浏览器)。 它还支持最新的Html5/CSS3标准&#xff0c;可以使用流行的CSS框架&#xff0c;如…

200、无线网桥与无线AP的带机量是多少?一篇文章搞明白

正文&#xff1a; 一个无线ap的带机量是多少&#xff1f;也有朋友提到无线网桥的带机量&#xff1f;这个我们之前有提到过&#xff0c;在了解他们的带机量的话&#xff0c;我们就不得不了解ap的性能指标了&#xff0c;那么本期我们来总结下带机量的问题。 一、选择AP前需要考虑…

用通俗易懂的大白话彻底搞明白mysql的数据类型以及mysql中的int(11),这个11到底是啥?

今天抽时间来讲一下mysql里的知识点&#xff0c;之前有不少人问过我&#xff0c;mysql中的int(11)&#xff0c;这个11到底是啥意思&#xff1f;是11位的意思吗&#xff1f;你是否也想过这个问题&#xff0c;是否也有这个疑问&#xff1f; ok&#xff0c;今天就展开来讲一下&am…

深度分析React源码中的合成事件

热身准备 明确几个概念 在React17.0.3版本中&#xff1a; 所有事件都是委托在id root的DOM元素中&#xff08;网上很多说是在document中&#xff0c;17版本不是了&#xff09;&#xff1b;在应用中所有节点的事件监听其实都是在id root的DOM元素中触发&#xff1b;React自…

【MySQL 第十一天 创建和存储|复合结构的存储|存储过程和函数的区别】

【MySQL 第十一天 创建和存储|复合结构的存储|存储过程和函数的区别】【1】mysql储存过程及语法结构【1.1】mysql过程体【2】mysql创建和使用存储过程【2.1】mysql创建无参的存储过程【2.2】mysql创建有参的输入输出存储过程【3】mysql删除存储过程【4】mysql创建复合结构的存储…

专精特新小巨人的认定条件

奖励&#xff1a;对新认定的专精特新“小巨人”企业&#xff0c;聊城市财政最高一次性奖励50万元。其他地区各有不同。 认定条件 专精特新“小巨人”企业认定需同时满足专、精、特、新、链、品六个方面指标。 (一)专业化指标&#xff1a;坚持专业化发展道路&#xff0c;长期…

大学生体育运动网页设计模板代码 DIV布局校园运动网页作业成品 HTML学校网页制作模板 学生简单体育运动网站设计成品

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

C++知识精讲14 | 算法篇之二分查找算法

博主主页&#xff1a;Cool Kid~Yu仙笙_C领域博主&#x1f984; 目录 二分查找定义 二分查找效率 二分查找与遍历的对比 二分查找的限制性 二分查找的限制性&#xff08;总结&#xff09; 二分查找搭建 循环实现二分查找 循环二分查找基本框架&#xff1a; 循环二分查找源码&am…

【苹果家庭iMessage推送】Aupperpushslcertificate或ProductPushsCertificate证书不可以过期

推荐内容IMESSGAE相关 作者推荐内容iMessage苹果推软件 *** 点击即可查看作者要求内容信息作者推荐内容1.家庭推内容 *** 点击即可查看作者要求内容信息作者推荐内容2.相册推 *** 点击即可查看作者要求内容信息作者推荐内容3.日历推 *** 点击即可查看作者要求内容信息作者推荐…

前端实现给文字添加动态背景

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…