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

news2024/9/22 12:56:47

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.coloradmin.cn/o/1381296.html

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

相关文章

Go 知多少?

作为一名已接触过其他语言的开发&#xff0c;再去学习一门新语言可比之前轻松不少&#xff0c; 语言之间存在很多相似点&#xff0c;但是新语言也有自己的不同点&#xff0c;通常我会先了解它与其他语言常遇到的不同点有哪些&#xff0c; 使自己先能够上手编写基础程序&#…

【LabVIEW FPGA入门】LabVIEW FPGA实现I2S解码器

该示例演示了如何使用 LabVIEW FPGA 解码 IS 信号。该代码可用于大多数支持高速数字输入的LabVIEW FPGA 目标&#xff08;例如R 系列、CompactRIO&#xff09;。IS 用于对系统和组件内的数字音频数据进行编码。例如&#xff0c;MP3 播放器或 DVD 播放器内部的数字音频通常使用 …

【b站咸虾米】新课uniapp零基础入门到项目打包(微信小程序/H5/vue/安卓apk)全掌握

课程地址&#xff1a;【新课uniapp零基础入门到项目打包&#xff08;微信小程序/H5/vue/安卓apk&#xff09;全掌握】 https://www.bilibili.com/video/BV1mT411K7nW/?p12&share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 三、vue语法 继续回到官…

飞桨分子动力学模拟-论文复现第六期:复现TorchMD

飞桨分子动力学模拟-论文复现第六期&#xff1a;复现TorchMD Paddle for MD 飞桨分子动力学模拟科学计算 复现论文-TorchMD: A deep learning framework for molecular simulations 本项目可在AIStudio一键运行&#xff1a;飞桨分子动力学模拟PaddleMD-复现TorchMD 【论文复…

浅析三种Anaconda虚拟环境创建方式和第三方包的安装

目录 引言 一、Anaconda虚拟环境创建方式 1. 使用conda命令创建虚拟环境 2. 使用conda-forge创建虚拟环境 3. 使用Miniconda创建虚拟环境 二、第三方包的安装和管理 1. 使用 pip 安装包&#xff1a; 2. 使用 conda 安装包&#xff1a; 三、结论与建议 引言 在当今的数…

Ceph入门到精通-通过 CloudBerry Explorer 管理对象bucket

简介 CloudBerry Explorer 是一款可用于管理对象存储&#xff08;Cloud Object Storage&#xff0c;COS&#xff09;的客户端工具。通过 CloudBerry Explorer 可实现将 COS 挂载在 Windows 等操作系统上&#xff0c;方便用户访问、移动和管理 COS 文件。 支持系统 支持 Wind…

【ceph】在虚拟环境中需要给osd所在的虚拟盘扩容操作

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

力扣每日一练(24-1-13)

如果用列表生成式&#xff0c;可以满足输出的型式&#xff0c;但是不满足题意&#xff1a; nums[:] [i for i in nums if i ! val]return len(nums) 题意要求是&#xff1a; 你需要原地修改数组&#xff0c;并且只使用O(1)的额外空间。这意味着我们不能创建新的列表&#xff…

2024年 13款 Linux 最强视频播放器

Linux视频播放器选择多样&#xff0c;如榛名、MPlayer、VLC等&#xff0c;功能强大、支持多格式&#xff0c;满足各类用户需求 Linux有许多非常强大的播放器&#xff0c;与windows最强视频播放器相比&#xff0c;几乎丝毫不逊色&#xff01; 一、榛名视频播放器 榛名视频播放…

Java副本的概念

在Java中&#xff0c;"副本"&#xff08;copy&#xff09;一词可以用于描述不同的概念&#xff0c;具体取决于上下文。以下是两个常见的用法&#xff1a; 对象的副本&#xff1a;在Java中&#xff0c;当你创建一个对象并将其赋值给另一个变量时&#xff0c;实际上是创…

遥感影像-语义分割数据集:高分卫星-云数据集详细介绍及训练样本处理流程

原始数据集详情 简介&#xff1a;该云数据集包括RGB三通道的高分辨率图像&#xff0c;包含高分一、高分二及宽幅数据集。 KeyValue卫星类型高分系列覆盖区域未知场景未知分辨率1m、2m、8m数量12000单张尺寸1024*1024原始影像位深8位标签图片位深8位原始影像通道数三通道标签图…

Nightingale 夜莺监控系统 - 部署篇(1)

Author&#xff1a;rab 官方文档&#xff1a;https://flashcat.cloud/docs 目录 一、概述二、架构2.1 中心机房架构2.2 边缘下沉式混杂架构 三、环境四、部署4.1 中心机房架构部署4.1.1 MySQL4.1.2 Redis4.1.3 Prometheus4.1.4 n9e4.1.5 Categraf4.1.6 验证4.1.7 配置数据源 4…

突然又对 Go 感兴趣,GOPATH entry cannot start with shell metacharacter 错误

打发无聊时间&#xff0c;水文一篇&#xff5e; 事情是这样的&#xff0c;因为我们上架的渠道包基本是定制化混淆出包&#xff0c; 混淆出包有一个关键点就是指定映射文件&#xff0c;映射文件的内容有一部分是使用外部工具在打包前按照一定规律随机生成包名、类名&#xff0c…

flutter使用get库管理路由,并设页面跳转动画和常见动画

get库还是非常强大的一个仓库&#xff0c;里面包含了非常常用的一些方法&#xff0c;比如路由管理&#xff0c;这是最常见和最常用的一个功能了&#xff0c;我们可以先配置一个路由对象&#xff0c;然后在里面配置路由列表&#xff0c;并且设置路由跳转方式。 第一种方式&…

vue3-计算属性

计算属性 模板中的表达式虽然方便&#xff0c;但也只能用来做简单的操作。如果在模板中写太多逻辑&#xff0c;会让模板变得臃肿&#xff0c;难以维护。 根据作者今年是否看过书展示不同信息 <script lang"ts" setup> import { ref, reactive } from "…

java实现局域网内视频投屏播放(五)视频搜索和投屏自动切换下一个

代码链接 这次对ui做了一些调整&#xff0c;整体分成了5个类别分别为 搜索设备播放任务已下载视频列表视频搜索下载任务列表 视频搜索 搜索 点击搜索后&#xff0c;会从执行所有VideoResolver实现类的search方法&#xff0c;将搜索到的结果汇总到一起&#xff0c;根据视频的…

设计模式⑤ :一致性

一、前言 有时候不想动脑子&#xff0c;就懒得看源码又不像浪费时间所以会看看书&#xff0c;但是又记不住&#xff0c;所以决定开始写"抄书"系列。本系列大部分内容都是来源于《 图解设计模式》&#xff08;【日】结城浩 著&#xff09;。该系列文章可随意转载。 …

node-sass@4.7.2 postinstall: `node scripts/build.js`

Can‘t find Python executable “D:\Python36\python.EXE“, you can set the PYTHON env variable.-CSDN博客 gyp ERR! build error gyp ERR! stack Error: C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe failed with exit code: 1 gyp ERR! stack at Chil…

【DDR】基于Verilog的DDR控制器的简单实现(一)——初始化

在FPGA中&#xff0c;大规模数据的存储常常会用到DDR。为了方便用户使用&#xff0c;Xilinx提供了DDR MIG IP核&#xff0c;用户能够通过AXI接口进行DDR的读写访问&#xff0c;然而MIG内部自动实现了许多环节&#xff0c;不利于用户深入理解DDR的底层逻辑。 本文以美光(Micro…

使用emu8086实现——顺序程序设计

一、实验目的 1. 掌握顺序程序设计方法 2. 掌握汇编语言编程设计方法。 二、实验内容 1.用查表的方法将一位十六进制数转换成它相应的ASCII码。 代码及注释&#xff1a; Data segment ;定义数据段Tab db 30h,31h,32h,33h,34h,35,36h,37h,38h,39h ;定义一个Tab的字节型…