二叉搜索树的插入、删除、修剪、构造操作(leetcode701、450、669、108)

目录

  • 1、leetcode 701. 二叉搜索树中的插入操作
    • 1、题目
    • 2、递归法
    • 3、迭代法
  • 2、leetcode 450. 二叉搜索树中的插入操作
    • 1、题目
    • 2、思路+递归法
    • 3、迭代法
    • 4、删除结点的两个方法以及注意点
  • 3、leetcode 669. 修剪二叉搜索树
    • 1、题目
    • 2、思考与递归
    • 3、迭代法
  • 4、leetcode 108. 将有序数组转换为二叉搜索树
    • 1、题目
    • 2、递归思路

1、leetcode 701. 二叉搜索树中的插入操作

1、题目

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。
输入数据 保证,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果 。
在这里插入图片描述
在这里插入图片描述

2、递归法

递归返回值以及参数
返回值为空,因为我们进行的是插入操作,不需要知道插在哪儿。
参数:当前结点cur以及需要插入的数值val
终止条件
如果当前指针为空,返回
单层逻辑
首先,按照val的大小,遍历左右子树。
如果当前结点的值大于val,且此结点没有左孩子,则可以将val构造的结点作为其左孩子。
如果当前结点的值小于val,且此结点没有右孩子,则可以将val构造的结点作为其右孩子。
否则return;
最后在大函数里面返回root就行了。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:void traversal(TreeNode* cur,int val){if(cur == NULL) return;if(cur->val > val) traversal(cur->left,val);if(cur->val < val) traversal(cur->right,val);if(cur->val > val && cur->left == NULL){//cout<<"插入左结点"<<endl;TreeNode* ans=new TreeNode(val);cur->left = ans;}if(cur->val < val && cur->right == NULL){//cout<<"插入右结点"<<endl;TreeNode* ans=new TreeNode(val);cur->right = ans;}return;}TreeNode* insertIntoBST(TreeNode* root, int val) {if(root == NULL){TreeNode* ans=new TreeNode(val);return ans;} traversal(root,val);return root;}
};

进行了一些修改后的代码

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:void traversal(TreeNode* cur,int val){if(cur == NULL) return;cout<<cur->val<<endl;if(cur->val > val){traversal(cur->left,val);if(cur->left == NULL){//cout<<"插入左结点"<<endl;TreeNode* ans=new TreeNode(val);cur->left = ans;}} if(cur->val < val){traversal(cur->right,val);if(cur->right == NULL){//cout<<"插入右结点"<<endl;TreeNode* ans=new TreeNode(val);cur->right = ans;}} return;}TreeNode* insertIntoBST(TreeNode* root, int val) {if(root == NULL){TreeNode* ans=new TreeNode(val);return ans;} traversal(root,val);return root;}
};

3、迭代法

在迭代法遍历的过程中,需要记录一下当前遍历的结点的父结点,这样才能进行插入结点操作。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode* insertIntoBST(TreeNode* root, int val) {if(root == NULL){TreeNode* ans=new TreeNode(val);return ans;} TreeNode* cur = root;TreeNode* parent = root;//记录上一个结点,否则不挖赋值新结点while(cur != NULL){parent = cur;if(cur->val > val) cur = cur->left;else cur = cur->right;}TreeNode* node = new TreeNode(val);if(val < parent->val) parent->left = node;        //利用parent的结点的值进行赋值else parent->right = node;return root;}
};

2、leetcode 450. 二叉搜索树中的插入操作

1、题目

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
在这里插入图片描述

2、思路+递归法

确定递归返回值和参数
通过递归返回值来删除结点。

TreeNode* deleteNode(TreeNode* root, int key)

确定终止条件
遇到空结点返回,说明了没有找到删除的结点。

if(root == NULL) return root;

确定单层逻辑
1、没有找到删除的结点,遍历到空结点直接返回。
2、找到了删除的结点:
【1】如果左右孩子都为空,直接删除这个结点,返回NULL为根结点
【2】如果左孩子为空,右孩子不为空,删除结点,右孩子补位,返回右孩子作为根结点
【3】如果右孩子为空,左孩子不为空,删除结点,左孩子补位,返回左孩子作为根结点
【4】如果左右孩子不为空,则将删除结点的左子树的头结点放到删除结点的右子树的最左边结点的左孩子上,返回删除结点右孩子,作为新的结点
代表的是中序遍历序列的下一个节点。即比当前节点大的最小节点在这里插入图片描述

        if(root->val == val){if(!root->left && !root->right) return root;else if(!root->left && root->right) return root->right;else if(root->left && !root->right) return root->left;else {TreeNode* cur = root->right;//找到右子树的最左边结点while(cur->left){cur = cur->left;}cur->left = root->left;TreeNode* tmp = root->left;root = root->right;delete tmp;return root;}}

将新的结点返回给上一层,上一层将其作为左或者右孩子:

if(root->val > key) root->left = deleteNode(root->left,key);
if(root->val < key) root->right= deleteNode(root->right,key);
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {if(root == NULL) return root;if(root->val == key){if(!root->left && !root->right) return NULL;    //直接删除结点,返回NULLelse if(!root->left && root->right) return root->right;else if(root->left && !root->right) return root->left;else {TreeNode* cur = root->right;//找到右子树的最左边结点while(cur->left){cur = cur->left;}cur->left = root->left;TreeNode* tmp = root;root = root->right;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;}
};

3、迭代法

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode* deleteOneNode(TreeNode* target){if(target == NULL) return NULL;if(target->right == NULL) return target->left;//如果right不为空,将left转移到right左子树的最左值。TreeNode* cur = target->right;while(cur->left) cur = cur->left;cur->left = target->left;//返回target的右子树,这样target结点就被删除了return target->right;}TreeNode* deleteNode(TreeNode* root, int key) {if(root == NULL) return NULL;TreeNode* cur = root;TreeNode* pre = NULL;       //记录cur的父结点,用来删除cur//找到目标结点while(cur){if(cur->val == key) break;pre = cur;if(cur->val > key) cur = cur->left;else cur = cur->right;}if(pre == NULL) return deleteOneNode(cur);      //如果搜索树只有头结点//删除右孩子还是删除左孩子,cur是父结点的左孩子还是右孩子if(pre->left && pre->left->val == key) pre->left = deleteOneNode(cur);if(pre->right && pre->right->val == key) pre->right = deleteOneNode(cur);return root;}
};

4、删除结点的两个方法以及注意点

1、移动树,找到目标结点直接对指针指向进行改变就结束了
2、覆盖值,找到目标结点先将目标结点的值和目标结点的右子树的最左边的值进行交换;交换后,继续去递归遍历树,直到再次遍历到目标结点的值,此时目标结点已经是叶子结点,左右孩子都为NULL,直接就返回NULL指针,实现了删除操作。(先覆盖值,后删除)
3、删除一个结点不能简单地置这个结点为空,我们需要修改父结点对此结点的指向!
例如我们需要将root->right覆盖掉root,不能root = root->right;而是应该这样写;
parent是root的父结点,child可能是左孩子也可能是右孩子,需要按照情况指定。

parent->child =  root->right; 

3、leetcode 669. 修剪二叉搜索树

1、题目

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
在这里插入图片描述
在这里插入图片描述

2、思考与递归

因为是要遍历整棵树并且做修改,其实不需要返回值也可以,我们也可以完成修剪。但有返回值,更方便,可以通过递归函数的返回值来移除结点

如果当前结点的值小于low,要对该结点的右孩子进行再次搜索,直到找到满足区间的结点或者空结点,最后返回right结点
如果当前结点的值大于high,要对该结点的左孩子进行再次搜索,直到找到满足区间的结点或者空结点,最后返回left结点。
自上而下遍历检查,直到整棵树遍历完。
依次遍历结点左右孩子,并将返回的结果作为当前结点的左右孩子。
最后返回当前结点。
返回的结点必然是val在区间内或者是空结点。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if(root == NULL) return root;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->left = trimBST(root->left,low,high);

用结点3的左孩子把下一层返回的结点0的右孩子(结点2)接住。此时结点3的右孩子就变成了结点2,将结点0从二叉树中移除了。
在这里插入图片描述

错误思路记录:先得到中序遍历的结果,然后修剪结果数组,然后通过修剪后的数组构造一个二叉搜索树.但是这个思路是错误的,因为单纯用一个中序遍历数组不能构造出唯一的二叉树,随意选择一种方法会改变二叉树的结构!
如:在这里插入图片描述low=1,high=3,预期结果应为:
在这里插入图片描述
但是我们这样做的结果是:
在这里插入图片描述

3、迭代法

剪枝的三个步骤:
1、将root移动到[L,R]范围内
2、剪枝左子树
3、剪枝右子树
个人认为这个思路比较好理解一点,虽然繁琐了些。

class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if(root == NULL) return root;//1、处理头结点,让root移动到[L,R]范围内while(root->val < low || root->val > high){if(root->val < low) root = root->right;       //小于L往右走else root = root->left;                     //大于R的往左走}TreeNode* cur = root;//此时root已经在[L,R]范围内了,处理左孩子元素小于L的情况while(cur){//如果左孩子比low小,就用左孩子的右孩子替代左孩子while(cur->left && cur->left->val < low){cur->left = cur->left->right;}//一直遍历左孩子cur = cur->left;}cur = root;//此时root'已经在范围内,处理右孩子元素大于R的情况while(cur){//如果右孩子比high大,就用右孩子的左孩子替代右孩子while(cur->right && cur->right->val > high){cur->right = cur->right->left;}//一直遍历右孩子cur = cur->right;}return root;}
};

4、leetcode 108. 将有序数组转换为二叉搜索树

1、题目

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
在这里插入图片描述
利用数组构造二叉树的本质就是寻找分割点,分割点作为当前结点,然后递归左区间和右区间。
有序数组的分割点我们选择数组中间位置的结点。
如果数组长度为偶数,中间结点有两个,两者取其一都可以,所以也就造成了答案不是唯一的。

2、递归思路

递归返回值以及参数
返回值为结点,我们要用返回值来构造中结点的左右孩子。
参数:数组,分割数组的左边界、分割数组的右边界,这里我们定义区间是左闭右闭的。

TreeNode* traversal(vector<int>& nums,int left,int right)

终止条件
由于区间的定义为左闭右闭,所以当left>right时,就是空结点了。

if(left > right) return nullptr;

确定单层逻辑
1、取中间值:int mid = left + ((right - left) / 2);这个是取靠左的中间值这样可以防止right\left过大时导致的数值越界。
2、以中间位置的元素构造结点:
TreeNode* root = new TreeNode(nums[mid]);
3、划分区间。root->left接住下一层左区间的构造结点,root->right接住下一层的右区间构造的结点。

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);

对于数组[-10,-3,0,5,9]观察遍历过程:

区间0,4
中间结点0
区间0,1
中间结点-10
区间0,-1
中间结点NULL
区间1,1
中间结点-3
区间1,0
中间结点NULL
区间2,1
中间结点NULL
区间3,4
中间结点5
区间3,2
中间结点NULL
区间4,4
中间结点9
区间4,3
中间结点NULL
区间5,4
中间结点NULL

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:TreeNode* traversal(vector<int>& nums,int left,int right){cout<<"区间"<<left<<","<<right<<endl;if(left > right){cout<<"中间结点"<<"NULL"<<endl;return nullptr;}int mid = left + ((right - left) / 2);cout<<"中间结点"<<nums[mid]<<endl;TreeNode* root = new TreeNode(nums[mid]);root->left = traversal(nums,left,mid-1);root->right = traversal(nums,mid+1,right);return root;}TreeNode* sortedArrayToBST(vector<int>& nums) {TreeNode* root = traversal(nums,0,nums.size()-1);return root;}
};

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

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

相关文章

模拟退火算法解决np_P和NP问题与解决方案| 演算法

模拟退火算法解决npP问题 (P Problems) P is the set of all the decision problems solvable by deterministic algorithms in polynomial time. P是多项式时间内确定性算法可解决的所有决策问题的集合。 NP问题 (NP Problems) NP is the set of all the decision problems t…

POJ2251Dungeon Master

http://poj.org/problem?id2251 题意 &#xff1a; 就是迷宫升级版&#xff0c;从以前的一个矩阵也就是一层&#xff0c;变为现在的L层&#xff0c;" . "是可以走&#xff0c;但是“#”不可以走&#xff0c;从S走到E&#xff0c;求最短的路径&#xff0c;若是找不到…

六、聚类算法

一、聚类概念 1&#xff0c;通俗易懂而言&#xff0c;聚类主要运用于无监督学习中&#xff0c;也就是将没有标签的东西如何分为几堆儿。 2&#xff0c;无监督学习即没有标签&#xff0c;不知道这些玩意到底是啥。当然&#xff0c;有监督学习就是由标签&#xff0c;我们是提前知…

【C++grammar】string类和array类

目录1、C11的string类1、创建 string 对象2、追加字符串append函数3、为字符串赋值assign函数4、at, clear, erase, and empty函数5、比较字符串compare()6、获取子串at() 、substr()函数7、搜索字符串find()8、插入和替换字符串insert() 、replace()9、字符串运算符10、string…

六、聚类算法实战

所有代码块都是在Jupyter Notebook下进行调试运行&#xff0c;前后之间都相互关联。 文中所有代码块所涉及到的函数里面的详细参数均可通过scikit-learn官网API文档进行查阅&#xff0c;这里我只写下每行代码所实现的功能&#xff0c;参数的调整读者可以多进行试验调试。多动手…

超图软件试用许可操作步骤_软件中的操作步骤

超图软件试用许可操作步骤The software comprises of three things: Program code, Documentation, and the Operating Procedures. The Program code is the entire software code. The Documentation is produced while the development of the software itself for the time…

【嵌入式系统】STM32配置FreeRTOS以及利用多线程完成流水灯、按键、蜂鸣器、数码管工作

目录1、利用STM32CubeMX配置FreeRTOS2、完成流水灯、按键、蜂鸣器数码管工作1、在gpio.c和.h文件里面书写并声明按键扫描和led、数码管子程序2、在freertos.c文件里面设置全局变量并且在各自任务中载入程序3、关于FreeRTOS的注意事项1、利用STM32CubeMX配置FreeRTOS 假设我们之…

css模糊_如何使用CSS模糊图像?

css模糊Introduction: 介绍&#xff1a; Sometimes even the professional developers tend to forget the various basic properties which can be applied to solve very simple problems, therefore the fundamentals of developing a website or web page should be very …

七、决策树算法和集成算法

一、决策树算法 Ⅰ&#xff0c;树模型 决策树&#xff1a;从根节点开始一步步走到叶子节点&#xff08;决策&#xff09; 所有的数据最终都会落到叶子节点&#xff0c;既可以做分类也可以做回归 对于分类&#xff1a;是由众数决定的&#xff0c;例如爷爷奶奶妈妈都是负数&…

leetcode 538. 把二叉搜索树转换为累加树 思考分析

题目 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下&#xff0c;二叉搜索树满足下列约束条件&…

SQL中GROUP BY语句与HAVING语句的使用

最近在学习SQL Server相关知识&#xff0c;一直不知道怎么使用GROUP BY语句&#xff0c;经过研究和练习&#xff0c;终于明白如何使用了&#xff0c;在此记录一下同时添加了一个自己举的小例子&#xff0c;通过写这篇文章来加深下自己学习的效果&#xff0c;还能和大家分享下&a…

八、决策树算法实验可视化展示

一、树模型的可视化展示 官网下载安装包 右击管理员身份运行&#xff0c;直接下一步即可。 配置环境变量&#xff1a; 将安装好的可视化软件的bin文件夹路径添加到系统环境变量Path下即可 打开cmd&#xff0c;输入dot -version&#xff0c;出现相关信息即安装成功 二、决策…

机器学习笔记:PCA的简单理解以及应用建议

用notability做的笔记&#xff0c;比较随意&#xff0c;对于第五点的PCA错误使用需要特别强调。 目录1、PCA与线性回归2、PCA主成分数量选择3、压缩重现4、PCA应用建议5、PCA的错误使用1、PCA与线性回归 2、PCA主成分数量选择 3、压缩重现 4、PCA应用建议 5、PCA的错误使用

一、信用卡卡号识别

一、思路分析 大体思路&#xff1a;首先拿到一张银行卡&#xff0c;我们得有银行卡号数字的0-9样式的模板&#xff0c;然后再通过不同数字的轮廓的外接矩形来进行匹配&#xff0c;最终识别出银行卡号所对应的数字。 银行卡数字模板&#xff1a; 银行卡信息&#xff1a; 拿到…

bootstrap网格系统_如何使用Bootstrap网格系统?

bootstrap网格系统In the last article, we learned how to create a simple page of Bootstrap? Now, we will learn what is "Grid System" in Bootstrap and how we can use or implement it in our bootstrap page? As you know bootstrap is a mobile-friendl…

回溯法初步

本文为参考公众号所做的笔记。 代码随想录原文 回溯法本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出我们想要的答案&#xff0c;所以它并不是一个高效的算法。但是由于有些问题本身能用暴力搜出来就不错了&#xff0c;所以回溯法也有很多的应用。 回溯法解决的问题…

二、文档扫描OCR

一、思路分析 首先&#xff0c;拿到一张文档&#xff0c;我们需要对文档进行预处理操作&#xff0c;再进行轮廓检测&#xff0c;因为就算拿到文档轮廓&#xff0c;但是这些轮廓也有可能是歪歪扭扭的&#xff0c;这时候需要通过一系列的透视变换操作&#xff0c;将文档摆正。通…

leetcode 77. 组合 思考分析

目录1、题目2、回溯法思路3、参考其他思路&#xff0c;更深入了解这个问题4、剪枝优化可能需要回顾到的知识文章&#xff1a;1、常用算法总结(穷举法、贪心算法、递归与分治算法、回溯算法、数值概率算法)2、回溯法初步删除vector容器中的对象元素的三种方法:pop_back, erase与…

三、全景拼接

一、项目所涉及到的一些知识点 Ⅰ&#xff0c;BF(Brute-Force)暴力匹配&#xff1a;把两张图像的特征点全部给算出来&#xff0c;然后使用归一化的欧氏距离比较这两张图像上特征点之间的大小关系&#xff0c;越小越相似。 SIFT算法 import cv2 import numpy as np import ma…

leetcode 216. 组合总和 III 思考分析

可能需要回顾的文章; leetcode 77. 组合 思考分析 1、题目 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数&#xff0c;并且每种组合中不存在重复的数字。 说明&#xff1a; 所有数字都是正整数。 解集不能包含重复的组合。 2、递归 这一题和之前…