代码随想录算法训练营第17天 | 第六章 二叉树 part07

2024-08-27 03:04

本文主要是介绍代码随想录算法训练营第17天 | 第六章 二叉树 part07,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第六章 二叉树part07

    • 235. 二叉搜索树的最近公共祖先
    • 701.二叉搜索树中的插入操作
    • 450.删除二叉搜索树中的节点

235. 二叉搜索树的最近公共祖先

相对于 二叉树的最近公共祖先 本题就简单一些了,因为 可以利用二叉搜索树的特性。

题目链接/文章讲解:[link](https://programmercarl.com/0235.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88.html )
视频讲解:[link](https://www.bilibili.com/video/BV1Zt4y1F7ww)
因为是有序树,所以 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p,我们从根节点搜索,第一次遇到 cur节点是数值在[q, p]区间中,即 节点5,此时可以说明 q 和 p 一定分别存在于 节点 5的左子树,和右子树中。
我们就从根节点溯源,都大就往左找,都小就往右找。第一个找到的说明介于两者之间。这就是公共祖先树。为什么不可能再往下找,如果再往下找,说明两个都比下面的结点小或者都大。但实际上,两个一个大一个小。不符合条件。
只要看懂下图为什么5是1和9的最近公共祖先。这题就很好理解了。
在这里插入图片描述

/*** 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if( root->val>p->val&&root->val>q->val)return lowestCommonAncestor( root->left, p, q) ;if( root->val<p->val&&root->val<q->val)return lowestCommonAncestor( root->right, p, q);else return root; }
};

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

本题比想象中的简单,大家可以先自己想一想应该怎么做,然后看视频讲解,就发现 本题为什么比较简单了。

题目链接/文章讲解:[link](https://programmercarl.com/0701.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E6%8F%92%E5%85%A5%E6%93%8D%E4%BD%9C.html)
视频讲解:[link](https://www.bilibili.com/video/BV1Et4y1c78Y)

/*** 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) {           return new TreeNode(val);}if(root->val>val)root->left =  insertIntoBST(root->left, val);if(root->val<val)root->right=insertIntoBST(root->right, val);return root;}
};

这题确实不难,因为它只让你插入,不管是不是平衡树或者树的的结构,所以,我们就一遍遍找,大了找左边,右边的肯定都比插入的大,不用管他们,小了找右边,左边的肯定都比插入的小,也不用管他们。一层层的把树的主干拆掉,肯定能找到适合的枝干插入叶子。另外,我在写的时候也在思考,前一个结点如何指向叶子节点的问题,其实也很好解决,直接返回给左右结点指针即可。如果下一个结点不是空结点,返回当前结点,是空结点,返回新的结点。写代码时越写越乱,后来整理就发现其实也不是很难,思路很清晰。

450.删除二叉搜索树中的节点

相对于 插入操作,本题就有难度了,涉及到改树的结构

题目链接/文章讲解:[link](https://programmercarl.com/0450.%E5%88%A0%E9%99%A4%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.html )
视频讲解:[link](https://www.bilibili.com/video/BV1tP41177us)
有以下五种情况:

第一种情况:没找到删除的节点,遍历到空节点直接返回了,完全不需要思考
找到删除的节点
第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点,最简单的删除
第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点,也很好理解,只有右孩子,右孩子得顶上去
第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点,也很好理解,只有左孩子,左孩子得顶上去,三种和四种情况差不多,完全可以类比
第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。我想到的方法是:用右子树的最小节点代替它,并删除最小节点。还是和他们的方法有点不太一样。代码随想录给出的方法是把左子树移动到右子树最小值下面。
第五种情况有点难以理解,看下面动画:
在这里插入图片描述

   2                                                     /     \
1        7/     \5          9 /  \       /    \ 4   6      8     102/     \
1        8/     \5          9 /   \         \ 4      6          10    

然后就是代码实现了:

  1. 利用二叉搜索树的性质,寻找要删除的节点:
    如果目标值比当前节点的值小,我们去左子树寻找。
    如果目标值比当前节点的值大,我们去右子树寻找。
    如果找到目标值,那就是我们要删除的节点。
  2. 情况一,找不到,直接返回原根节点
  3. 情况二,叶子节点,直接删掉
  4. 情况三,情况四,只有一个子节点,让子结点代替父节点,并删除
  5. 情况5:节点有两个子节点
    将删除节点的左孩子放到删除节点的右子树的最左面节点的左孩子上,要删除的节点的右孩子为新的根节点。右面顶替,左边依附
    当节点有两个子节点时,比较复杂。我们需要找到它的中序后继,也就是右子树中最小的节点来替代它。找到右子树中的最小节点。用这个最小节点的值替换要删除的节点。删除右子树中的最小节点。右边最小顶替中间。
    代码看着挺长,实际上只要先把框架搭好,最后一步步写代码
    大框架用来找到目标结点
    相等以后的中框架用来处理

需要使用临时指针 TreeNode* tmp = root 来保存当前要删除的节点,因为我们先用一个临时指针 tmp 保存原本 root 所指向的节点,然后让 root 指向新的位置(即它的右子节点)。最后,释放掉 tmp,从而确保内存不会泄漏。

TreeNode* pre = root;
TreeNode* cur = root->right;
while (cur->left != nullptr) {pre = cur;cur = cur->left;
}
root->val = cur->val;
pre->left = nullptr;
delete cur;

我最开始写的代码块如上所示,报错误了有一个潜在的 heap-use-after-free 问题,程序在释放内存后尝试访问该内存地址。错误根源:
循环里找到右子树中最小的节点 (cur) 并将其值赋给 root,然后你删除了该最小节点。如果 cur 节点是 pre->left 的子节点(即 cur 有左孩子),pre->left 设置为 nullptr,然后删除 cur。但如果 cur 本身就是 root->right(即 cur 没有左孩子,而是直接在右子树的根),pre 将始终等于 root,那么 pre->left = nullptr; 实际上不会移除正确的链接,导致错误。
另外当找到右子树最小的节点(即 cur),你设置了 pre->left = nullptr;但这是不正确的处理方式。如果 cur 节点有右子树,那么你需要将pre->left 指向 cur->right 而不是简单地设置为 nullptr。
完整代码如下:还是需要点耗脑子。避坑。我的思路比较简单直白易懂,直接一个替代,但是会出现很多考虑不到的问题,但是代码随想录的方法就复杂(其实也没复杂多少),代码更好实现。

/*** 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 == nullptr) return root; if (root->val == key) {if (root->left == nullptr && root->right == nullptr) {     delete root;return nullptr;}    else if (root->left == nullptr) {auto retNode = root->right;delete root;return retNode;}else if (root->right == nullptr) {auto retNode = root->left;delete root;return retNode;}      else {TreeNode* cur = root->right; while(cur->left != nullptr) {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;}
};

这篇关于代码随想录算法训练营第17天 | 第六章 二叉树 part07的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1110448

相关文章

pandas实现数据concat拼接的示例代码

《pandas实现数据concat拼接的示例代码》pandas.concat用于合并DataFrame或Series,本文主要介绍了pandas实现数据concat拼接的示例代码,具有一定的参考价值,... 目录语法示例:使用pandas.concat合并数据默认的concat:参数axis=0,join=

C#代码实现解析WTGPS和BD数据

《C#代码实现解析WTGPS和BD数据》在现代的导航与定位应用中,准确解析GPS和北斗(BD)等卫星定位数据至关重要,本文将使用C#语言实现解析WTGPS和BD数据,需要的可以了解下... 目录一、代码结构概览1. 核心解析方法2. 位置信息解析3. 经纬度转换方法4. 日期和时间戳解析5. 辅助方法二、L

Python使用Code2flow将代码转化为流程图的操作教程

《Python使用Code2flow将代码转化为流程图的操作教程》Code2flow是一款开源工具,能够将代码自动转换为流程图,该工具对于代码审查、调试和理解大型代码库非常有用,在这篇博客中,我们将深... 目录引言1nVflRA、为什么选择 Code2flow?2、安装 Code2flow3、基本功能演示

IIS 7.0 及更高版本中的 FTP 状态代码

《IIS7.0及更高版本中的FTP状态代码》本文介绍IIS7.0中的FTP状态代码,方便大家在使用iis中发现ftp的问题... 简介尝试使用 FTP 访问运行 Internet Information Services (IIS) 7.0 或更高版本的服务器上的内容时,IIS 将返回指示响应状态的数字代

MySQL 添加索引5种方式示例详解(实用sql代码)

《MySQL添加索引5种方式示例详解(实用sql代码)》在MySQL数据库中添加索引可以帮助提高查询性能,尤其是在数据量大的表中,下面给大家分享MySQL添加索引5种方式示例详解(实用sql代码),... 在mysql数据库中添加索引可以帮助提高查询性能,尤其是在数据量大的表中。索引可以在创建表时定义,也可

使用C#删除Excel表格中的重复行数据的代码详解

《使用C#删除Excel表格中的重复行数据的代码详解》重复行是指在Excel表格中完全相同的多行数据,删除这些重复行至关重要,因为它们不仅会干扰数据分析,还可能导致错误的决策和结论,所以本文给大家介绍... 目录简介使用工具C# 删除Excel工作表中的重复行语法工作原理实现代码C# 删除指定Excel单元

Python实现一键PDF转Word(附完整代码及详细步骤)

《Python实现一键PDF转Word(附完整代码及详细步骤)》pdf2docx是一个基于Python的第三方库,专门用于将PDF文件转换为可编辑的Word文档,下面我们就来看看如何通过pdf2doc... 目录引言:为什么需要PDF转Word一、pdf2docx介绍1. pdf2docx 是什么2. by

Spring Security介绍及配置实现代码

《SpringSecurity介绍及配置实现代码》SpringSecurity是一个功能强大的Java安全框架,它提供了全面的安全认证(Authentication)和授权(Authorizatio... 目录简介Spring Security配置配置实现代码简介Spring Security是一个功能强

通过cmd获取网卡速率的代码

《通过cmd获取网卡速率的代码》今天从群里看到通过bat获取网卡速率两段代码,感觉还不错,学习bat的朋友可以参考一下... 1、本机有线网卡支持的最高速度:%v%@echo off & setlocal enabledelayedexpansionecho 代码开始echo 65001编码获取: >

Java集成Onlyoffice的示例代码及场景分析

《Java集成Onlyoffice的示例代码及场景分析》:本文主要介绍Java集成Onlyoffice的示例代码及场景分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 需求场景:实现文档的在线编辑,团队协作总结:两个接口 + 前端页面 + 配置项接口1:一个接口,将o