C++算法学习心得五.二叉树(4)

1.二叉搜索树中的插入操作(701题)

题目描述:给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据保证,新值和原始二叉搜索树中的任意节点值都不同。

递归法: 只要遍历二叉搜索树,找到空节点 插入元素就可以了,有返回值的话,可以利用返回值完成新加入的节点与其父节点的赋值操作

class Solution {
public:TreeNode* insertIntoBST(TreeNode* root, int val) {//节点为空,节点就是需要插入节点位置,并且把插入的节点返回if(root == NULL){TreeNode* node = new TreeNode(val);//新创建一个节点return node;//把这个节点返回}if(root->val > val)root->left = insertIntoBST(root->left,val);//根节点值大于目标值,左,根节点小于目标值,右if(root->val < val)root->right = insertIntoBST(root->right,val);return root;}
};

迭代法:使用了pre和cur两个指针的方法来实现迭代

class Solution {
public://双指针的想法来实现,前一个节点和后一个节点来同时移动TreeNode* insertIntoBST(TreeNode* root, int val) {if(root == NULL){TreeNode* node = new TreeNode(val);return node;}TreeNode* pre = root;//记录上一个节点,对新的节点赋值操作TreeNode* cur = root;while(cur != NULL){pre = cur;if(cur->val > val)cur = cur->left;else cur = cur->right;}TreeNode* node = new TreeNode(val);if(val < pre->val) pre->left = node;//使用pre节点对其赋值else pre->right = node;return root;}
};

2.删除二叉搜索树中的节点(450题)

题目描述:

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点; 如果找到了,删除它。 说明: 要求算法时间复杂度为 $O(h)$,h 为树的高度。

递归法:需要考虑好根节点情况,判断要删除节点左右孩子情况,

  • 第一种情况:没找到删除的节点,遍历到空节点直接返回了
  • 找到删除的节点
    • 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
    • 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
    • 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
    • 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {if(root == nullptr)return root;//没找到删除的节点,遇到空节点直接返回,//根节点的值等于目标值if(root->val == key){if(root->left == nullptr)return root->right;//左孩子为空,返回右节点else if(root->right == nullptr)return root->left;//右孩子为空返回左节点else{TreeNode* cur = root->right;//查找右子树上的最左面的节点//找最左面的节点while(cur->left!=nullptr){cur = cur->left;}cur->left = root->left;//删除的节点左子树放在cur左子树上TreeNode* tmp = root;//保存root节点root = root->right;//返回右孩子作新的root节点delete tmp;//删除节点return root;}} if(root->val > key)root->left = deleteNode(root->left,key);//左if(root->val < key)root->right = deleteNode(root->right,key);//右return root;}
};

普通二叉树的递归方式:

代码中目标节点(要删除的节点)被操作了两次:

  • 第一次是和目标节点的右子树最左面节点交换。
  • 第二次直接被NULL覆盖了。
class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {if (root == nullptr) return root;if (root->val == key) {if (root->right == nullptr) { // 这里第二次操作目标值:最终删除的作用return root->left;}TreeNode *cur = root->right;while (cur->left) {cur = cur->left;}swap(root->val, cur->val); // 这里第一次操作目标值:交换目标值其右子树最左面节点。}root->left = deleteNode(root->left, key);root->right = deleteNode(root->right, key);return root;}
};

迭代法:

class Solution {
private:TreeNode* deleteNodeOperation(TreeNode* target){if(target == nullptr)return target;if(target->right == nullptr)return target->left;TreeNode* cur = target->right;while(cur->left){cur = cur->left;}cur->left = target->left;return target->right;}
public:TreeNode* deleteNode(TreeNode* root, int key) {if(root == nullptr)return root;TreeNode* cur = root;TreeNode* pre = nullptr;while(cur){if(cur->val == key)break;pre = cur;if(cur->val > key)cur = cur->left;else cur = cur->right;}if(pre == nullptr){return deleteNodeOperation(cur);}if(pre->left && pre->left->val == key){pre->left = deleteNodeOperation(cur);}if(pre->right && pre->right->val == key){pre->right = deleteNodeOperation(cur);}return root;}
};

因为二叉搜索树添加节点只需要在叶子上添加就可以的,不涉及到结构的调整,而删除节点操作涉及到结构的调整。主要难点在于如何处理左右节点都不为空情况

3.修剪二叉搜索树 (669题)

题目描述:

给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。

递归法:那么将节点0的右孩子 节点2 直接赋给 节点3的左孩子就可以了(就是把节点0从二叉树中移除) ,上一个节点接收下面返回值直接返回

class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if(root == nullptr)return nullptr;//空节点返回if(root->val < low){TreeNode* right = trimBST(root->right,low,high);//寻找在区间内的节点,return right;}if(root->val > high){TreeNode* left = trimBST(root->left,low,high);//寻找区间内的节点return left;}root->left = trimBST(root->left,low,high);//左root->right = trimBST(root->right,low,high);//右return root;}
};

 迭代法:二叉搜索树,有序,不用栈去辅助就可实现

在剪枝的时候,可以分为三步:

  • 将root移动到[L, R] 范围内,注意是左闭右闭区间
  • 剪枝左子树
  • 剪枝右子树
class Solution {
public:TreeNode* trimBST(TreeNode* root, int L, int R) {if (!root) return nullptr;// 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭while (root != nullptr && (root->val < L || root->val > R)) {if (root->val < L) root = root->right; // 小于L往右走else root = root->left; // 大于R往左走}TreeNode *cur = root;// 此时root已经在[L, R] 范围内,处理左孩子元素小于L的情况while (cur != nullptr) {while (cur->left && cur->left->val < L) {cur->left = cur->left->right;}cur = cur->left;}cur = root;// 此时root已经在[L, R] 范围内,处理右孩子大于R的情况while (cur != nullptr) {while (cur->right && cur->right->val > R) {cur->right = cur->right->left;}cur = cur->right;}return root;}
};

4.将有序数组转换为二叉搜索树(108题)

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

递归法: 用数组的下标来操作数组,递归时候遵循循环不变量原则

class Solution {
private:TreeNode* traversal(vector<int>& nums,int left,int right){if(left > right)return nullptr;//终止条件int mid = left + ((right-left)/2);//找中间的根节点TreeNode* root = new TreeNode(nums[mid]);//创建一个树的根节点root->left = traversal(nums,left,mid-1);//左子树继续递归root->right = traversal(nums,mid + 1,right);//右子树递归return root;}
public:TreeNode* sortedArrayToBST(vector<int>& nums) {TreeNode* root = traversal(nums,0,nums.size()-1);//调用递归return root;}
};

迭代法:使用三个队列实现,一个队列放遍历的节点,一个队列放左区间下标,一个队列放右区间下标。

class Solution {
public:TreeNode* sortedArrayToBST(vector<int>& nums) {if (nums.size() == 0) return nullptr;TreeNode* root = new TreeNode(0);   // 初始根节点queue<TreeNode*> nodeQue;           // 放遍历的节点queue<int> leftQue;                 // 保存左区间下标queue<int> rightQue;                // 保存右区间下标nodeQue.push(root);                 // 根节点入队列leftQue.push(0);                    // 0为左区间下标初始位置rightQue.push(nums.size() - 1);     // nums.size() - 1为右区间下标初始位置while (!nodeQue.empty()) {TreeNode* curNode = nodeQue.front();nodeQue.pop();int left = leftQue.front(); leftQue.pop();int right = rightQue.front(); rightQue.pop();int mid = left + ((right - left) / 2);curNode->val = nums[mid];       // 将mid对应的元素给中间节点if (left <= mid - 1) {          // 处理左区间curNode->left = new TreeNode(0);nodeQue.push(curNode->left);leftQue.push(left);rightQue.push(mid - 1);}if (right >= mid + 1) {         // 处理右区间curNode->right = new TreeNode(0);nodeQue.push(curNode->right);leftQue.push(mid + 1);rightQue.push(right);}}return root;}
};

5.把二叉搜索树转换为累加树(538题)

题目描述:

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

节点的左子树仅包含键 小于 节点键的节点。 节点的右子树仅包含键 大于 节点键的节点。 左右子树也必须是二叉搜索树。

 递归法:从树中可以看出累加的顺序是右中左,所以我们需要反中序遍历这个二叉树,然后顺序累加就可以了

一个pre指针记录当前遍历节点cur的前一个节点

class Solution {
private:int pre = 0;//整形记录前一个一个值void traversal(TreeNode* cur){if(cur == NULL)return ;traversal(cur->right);//反中序遍历实现,右cur->val += pre;//中pre = cur->val;//traversal(cur->left);//左}
public:TreeNode* convertBST(TreeNode* root) {pre = 0;traversal(root);return root;}
};

迭代法:中序迭代的模版

class Solution {
private:int pre; // 记录前一个节点的数值void traversal(TreeNode* root) {stack<TreeNode*> st;TreeNode* cur = root;while (cur != NULL || !st.empty()) {if (cur != NULL) {st.push(cur);cur = cur->right;   // 右} else {cur = st.top();     // 中st.pop();cur->val += pre;pre = cur->val;cur = cur->left;    // 左}}}
public:TreeNode* convertBST(TreeNode* root) {pre = 0;traversal(root);return root;}
};

总结:

二叉搜索树的插入操作:此题需要做的就是找到叶子节点然后再根据二叉搜索树的特性来,然后去接收左子树和右子树,向上返回,再根据二叉搜索树的特性来完成实现,根据值来遍历,迭代法使用两个节点的方法来实现,

删除二叉搜索树中的节点:这里要考虑递归法的终止条件设定,考虑好左右子树是否为空的几种情况,再对这些情况做出相应的处理,主要注意左右子树不为空的情况如何去处理,需要在右子树的最左子树的节点去将左子树接收,然后把值去掉接收右子树即可,迭代法去实现

修剪二叉搜索树:利用了删除二叉搜索树的方法来实现,就是需要将减枝下来的节点去接收,然后再进行迭代,注意二叉搜索树是根据值来递归的,因为其值有大小有序,所以可以按照这个方向,来进行递归,

将有序数组转换成二叉搜索树:将一个数组按照中间数据进行分割,然后再进行左数组和右数组进行分别来进行递归,按照取中间节点操作进行,然后按照节点值来进行递归,最好按照数组的下标来进行操作就好了,

把二叉搜索树转换为累加树:累加树来进行数值的反中序遍历,先进行右遍历,然后处理中间节点的操作,之后再进行左处理,注需要使用整形的数来接收前一个节点的值,来实现,其实和双指针的想法差不多

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

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

相关文章

【Python机器学习】SVM——线性模型与非线性特征

SVM&#xff08;核支持向量机&#xff09;是一种监督学习模型&#xff0c;是可以推广到更复杂模型的扩展&#xff0c;这些模型无法被输入空间的超平面定义。 线模型在低维空间中可能非常受限&#xff0c;因为线和平面的灵活性有限&#xff0c;但是有一种方式可以让线性模型更加…

Linux系统中的IP地址、主机名、和域名解析

1.IP地址 每一台联网的电脑都会有一个地址&#xff0c;用于和其它计算机进行通讯 IP地址主要有2个版本&#xff0c;V4版本和V6版本&#xff08;V6很少用&#xff0c;暂不涉及&#xff09; IPv4版本的地址格式是&#xff1a;a.b.c.d&#xff0c;其中abcd表示0~255的数字&…

echarts x轴下增加一组数据的实现方法

实现效果&#xff1a; 关键代码 xAxis: [{type: category,axisTick:{show: false},axisLine:{show: false},axisLabel:{align:center,},data: [9-w2, 9-w3, 343,9-w2, 9-w3, 343]},{type: category,name: 排比变化,nameTextStyle: {verticalAlign: "left",padding:[…

迈入AI智能时代!ChatGPT国内版免费AI助手工具 peropure·AI正式上线 一个想法写一首歌?这事AI还真能干!

号外&#xff01;前几天推荐的Peropure.Ai迎来升级&#xff0c;现已支持联网模式&#xff0c;回答更新更准&#xff0c;欢迎注册体验&#xff1a; https://sourl.cn/5T74Hu 相信很多人都有过这样的想法&#xff0c;有没有一首歌能表达自己此时此刻的心情&#xff1a; 当你在深…

虚幻UE 特效-Niagara特效初识

虚幻的Niagara特效系统特别的强大&#xff0c;可以为开发者提供丰富的视觉效果&#xff01; 本篇笔记对Niagara系统进行初步的学习探索 文章目录 前言一、Niagara四大核心组件二、粒子发射器和粒子系统1、粒子发射器的创建2、粒子系统的创建3、Niagara系统的使用 总结 前言 在…

SpringBoot之优化高并发场景下的HttpClient并提升QPS

HttpClient优化思路 使用连接池&#xff08;简单粗暴&#xff09; 长连接优化&#xff08;特殊业务场景&#xff09; httpclient和httpget复用 合理的配置参数&#xff08;最大并发请求数&#xff0c;各种超时时间&#xff0c;重试次数&#xff09; 异步请求优化&#xff0…

个人博客教程(Typora官方免费版)

教程 链接&#xff1a;https://pan.baidu.com/s/1kVk3wxrcAPkIy8VrX7CK7g?pwdigiz 提取码&#xff1a;igiz 其实下面的教程都可以通过右键选择你想要的文本来实现&#xff0c;但是掌握基本的语法可以更快&#xff0c;如果看不懂我写的是什么东西可以查看非常简单的入门教程M…

解密Mybatis-Plus:优雅简化你的数据访问层!

目录 1、引言 2、什么是Mybatis-Plus 3、Mybatis-Plus的特点和优势 4、安装和配置Mybatis-Plus 5、使用Mybatis-Plus进行数据库操作 6、Mybatis-Plus的高级功能 7、Mybatis-Plus的扩展和插件 8、与Spring Boot集成 9、结语 1、引言 Mybatis-Plus是一个强大而优雅的Jav…

科研学习|论文解读——信息世界映射方法

题目&#xff1a;信息世界映射的下一步是什么&#xff1f;在情境中理解信息行为/实践的国际化和多学科方法&#xff08;What is next for information world mapping? International and multidisciplinary approaches to understanding information behaviors/ practices in …

Feature Fusion for Online Mutual KD

paper&#xff1a;Feature Fusion for Online Mutual Knowledge Distillation official implementation&#xff1a;https://github.com/Jangho-Kim/FFL-pytorch 本文的创新点 本文提出了一个名为特征融合学习&#xff08;Feature Fusion Learning, FFL&#xff09;的框架&…

进程的状态

进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转换。在三态模型 中&#xff0c;进程状态分为三个基本状态&#xff0c;即就绪态&#xff0c;运行态&#xff0c;阻塞态。在五态模型中&#xff0c;进程分为新建态、就绪态&#xff0c;运行态&#x…

井盖异动传感器,守护脚下安全

随着城市化进程的加速&#xff0c;城市基础设施的安全问题日益受到关注。其中&#xff0c;井盖作为城市地下管道的重要入口&#xff0c;其安全问题不容忽视。然而&#xff0c;传统的井盖监控方式往往存在盲区&#xff0c;无法及时发现井盖的异常移动。为此&#xff0c;我们推出…

xtu oj 1329 连分式

题目描述 连分式是形如下面的分式&#xff0c;已知a,b和迭代的次数n&#xff0c;求连分式的值。 输入 第一行是一个整数T(1≤T≤1000)&#xff0c;表示样例的个数。 每行一个样例&#xff0c;为a,b,n(1≤a,b,n≤9) 输出 每行输出一个样例的结果&#xff0c;使用x/y分式表达…

Hive数据定义(2)

hive数据定义是hive的基础知识&#xff0c;所包含的知识点有&#xff1a;数据仓库的创建、数据仓库的查询、数据仓库的修改、数据仓库的删除、表的创建、表的删除、内部表、外部表、分区表、桶表、表的修改、视图。在上一篇文章中介绍了一部分知识点&#xff0c;在本篇文章中将…

Vue2.脚手架

全局安装&#xff1a;npm i vue/cli -g检查是否成功安装&#xff1a;vue --version新建项目&#xff1a;vue create 项目名 通过nodejs安装的时候&#xff0c;可以直接代理和仓库&#xff0c;~/.npmrc文件内容如下&#xff1a; proxysocks5://127.0.0.1:7897 registryhttps:/…

Kafka的核心原理

Topic的分区和副本机制 分区有什么用呢? 作用&#xff1a; 1- 避免单台服务器容量的限制: 每台服务器的磁盘存储空间是有上限。Topic分成多个Partition分区&#xff0c;可以避免单个Partition的数据大小过大&#xff0c;导致服务器无法存储。利用多台服务器的存储能力&#…

Matlab数学建模算法之模拟退火算法(SA)详解

&#x1f517; 运行环境&#xff1a;Matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 &#x1f510;#### 防伪水印——左手の明天 ####&#x1f510; &#x1f497; 大家…

WEB 3D技术 three.js 聚光灯

本文 我们来说说 点光源和聚光灯 点光源 就像一个电灯泡一样 想四周发散光 而聚光灯就像手电筒一样 像一个方向射过去 距离越远范围越大 光越弱 我们先来看一个聚光灯的效果 我们可以编写代码如下 import ./style.css import * as THREE from "three"; import { O…

【JavaScript】深度理解js的函数(function、Function)

简言 学了这么久的JavaScript&#xff0c;函数在JavaScript中最常用之一&#xff0c;如果你不会函数&#xff0c;你就不会JavaScript。 函数就是Function对象&#xff0c;一个函数是可以通过外部代码调用的一个“子程序”&#xff0c;它是头等&#xff08;first-class&#xf…

linux 如何创建文件

我们在写一些教程的时候&#xff0c;经常会需要创建一些用于演示的文档&#xff0c;这些文档往往需要填充一些不特定的内容。那么如何快速的创建演示用的文档呢&#xff1f; docfaker.py docfaker.py是一个py脚本&#xff0c;用于创建一个简单的txt文档&#xff0c;docfaker.…