代码随想录拓展day5 129. 求根节点到叶节点数字之和;1382.将二叉搜索树变平衡;100. 相同的树;116. 填充每个节点的下一个右侧节点指针

news2024/9/27 7:23:01

代码随想录拓展day5 129. 求根节点到叶节点数字之和;1382.将二叉搜索树变平衡;100. 相同的树;116. 填充每个节点的下一个右侧节点指针

全部都是关于二叉树的题目,对二叉树的遍历方式又是一个复习。

129. 求根节点到叶节点数字之和

https://leetcode.cn/problems/sum-root-to-leaf-numbers/

关键是把数组转数字的这个操作要熟悉。其余了还是类似回溯的二叉树递归的方法。

思路

首先思路很明确,就是要遍历整个树把更节点到叶子节点组成的数字相加。

那么先按递归三部曲来分析:

递归三部曲

  • 确定递归函数返回值及其参数

这里我们要遍历整个二叉树,且需要要返回值做逻辑处理,所有返回值为void。

参数只需要把根节点传入,此时还需要定义两个全局遍历,一个是result,记录最终结果,一个是vector path。

为什么用vector类型(就是数组)呢? 因为用vector方便我们做回溯!

所以代码如下:

int result;
vector<int> path;
void traversal(TreeNode* cur) 
  • 确定终止条件

递归什么时候终止呢?

当然是遇到叶子节点,此时要收集结果了,通知返回本层递归,因为单条路径的结果使用vector,我们需要一个函数vectorToInt把vector转成int。

终止条件代码如下:

if (!cur->left && !cur->right) { // 遇到了叶子节点
    result += vectorToInt(path);
    return;
}

这里vectorToInt函数就是把数组转成int,代码如下:

int vectorToInt(const vector<int>& vec) {
    int sum = 0;
    for (int i = 0; i < vec.size(); i++) {
        sum = sum * 10 + vec[i];
    }
    return sum;
}
  • 确定递归单层逻辑

本题其实采用前中后序都不无所谓, 因为也没有中间几点的处理逻辑。

这里主要是当左节点不为空,path收集路径,并递归左孩子,右节点同理。

但别忘了回溯

如图:

在这里插入图片描述

代码如下:

                 // 中
if (cur->left) { // 左 (空节点不遍历)
    path.push_back(cur->left->val);
    traversal(cur->left);    // 递归
    path.pop_back();         // 回溯
}
if (cur->right) { // 右 (空节点不遍历)
    path.push_back(cur->right->val);
    traversal(cur->right);   // 递归
    path.pop_back();         // 回溯
}

这里要注意回溯和递归要永远在一起,一个递归,对应一个回溯,是一对一的关系,有的同学写成如下代码:

if (cur->left) { // 左 (空节点不遍历)
    path.push_back(cur->left->val);
    traversal(cur->left);    // 递归
}
if (cur->right) { // 右 (空节点不遍历)
    path.push_back(cur->right->val);
    traversal(cur->right);   // 递归
}
path.pop_back();         // 回溯

把回溯放在花括号外面了,世界上最遥远的距离,是你在花括号里,而我在花括号外! 这就不对了。

整体C++代码

关键逻辑分析完了,整体C++代码如下:

class Solution {
private:
    int result;
    vector<int> path;
    // 把vector转化为int
    int vectorToInt(const vector<int>& vec) {
        int sum = 0;
        for (int i = 0; i < vec.size(); i++) {
            sum = sum * 10 + vec[i];
        }
        return sum;
    }
    void traversal(TreeNode* cur) {
        if (!cur->left && !cur->right) { // 遇到了叶子节点
            result += vectorToInt(path);
            return;
        }

        if (cur->left) { // 左 (空节点不遍历)
            path.push_back(cur->left->val);     // 处理节点
            traversal(cur->left);               // 递归
            path.pop_back();                    // 回溯,撤销
        }
        if (cur->right) { // 右 (空节点不遍历)
            path.push_back(cur->right->val);    // 处理节点
            traversal(cur->right);              // 递归
            path.pop_back();                    // 回溯,撤销
        }
        return ;
    }
public:
    int sumNumbers(TreeNode* root) {
        path.clear();
        if (root == nullptr) return 0;
        path.push_back(root->val);
        traversal(root);
        return result;
    }
};

1382.将二叉搜索树变平衡

https://leetcode.cn/problems/balance-a-binary-search-tree/

本来以为要原地调整,但是可以把搜索二叉树转成有序数组,这就成了一道还原平衡二叉树的题目了。

思路

这道题目,可以中序遍历把二叉树转变为有序数组,然后在根据有序数组构造平衡二叉搜索树。

代码如下:

class Solution {
private:
    vector<int> vec;
    // 有序树转成有序数组
    void traversal(TreeNode* cur) {
        if (cur == nullptr) {
            return;
        }
        traversal(cur->left);
        vec.push_back(cur->val);
        traversal(cur->right);
    }
    // 有序数组转平衡二叉树
    TreeNode* getTree(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 = getTree(nums, left, mid - 1);
        root->right = getTree(nums, mid + 1, right);
        return root;
    }

public:
    TreeNode* balanceBST(TreeNode* root) {
        traversal(root);
        return getTree(vec, 0, vec.size() - 1);
    }
};

100. 相同的树

https://leetcode.cn/problems/same-tree/

和验证对称二叉树有些类似。

思路

我们讲到对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。

理解这一本质之后,就会发现,求二叉树是否对称,和求二叉树是否相同几乎是同一道题目。

递归三部曲中:

  1. 确定递归函数的参数和返回值

我们要比较的是两个树是否是相互相同的,参数也就是两个树的根节点。

返回值自然是bool类型。

代码如下:

bool compare(TreeNode* tree1, TreeNode* tree2)
  1. 确定终止条件

要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。

节点为空的情况有:

  • tree1为空,tree2不为空,不对称,return false
  • tree1不为空,tree2为空,不对称 return false
  • tree1,tree2都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是tree1和tree2不为空的时候:

  • tree1、tree2都不为空,比较节点数值,不相同就return false

此时tree1、tree2节点不为空,且数值也不相同的情况我们也处理了。

代码如下:

if (tree1 == NULL && tree2 != NULL) return false;
else if (tree1 != NULL && tree2 == NULL) return false;
else if (tree1 == NULL && tree2 == NULL) return true;
else if (tree1->val != tree2->val) return false; // 注意这里我没有使用else
  1. 确定单层递归的逻辑
  • 比较二叉树是否相同 :传入的是tree1的左孩子,tree2的右孩子。
  • 如果左右都相同就返回true ,有一侧不相同就返回false 。

代码如下:

bool left = compare(tree1->left, tree2->left);   // 左子树:左、 右子树:左
bool right = compare(tree1->right, tree2->right);  // 左子树:右、 右子树:右
bool isSame = left && right;                    // 左子树:中、 右子树:中(逻辑处理)
return isSame;

最后递归的C++整体代码如下:

class Solution {
public:
    bool compare(TreeNode* tree1, TreeNode* tree2) {
        if (tree1 == NULL && tree2 != NULL) return false;
        else if (tree1 != NULL && tree2 == NULL) return false;
        else if (tree1 == NULL && tree2 == NULL) return true;
        else if (tree1->val != tree2->val) return false; // 注意这里我没有使用else

        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        bool left = compare(tree1->left, tree2->left);   // 左子树:左、 右子树:左
        bool right = compare(tree1->right, tree2->right);  // 左子树:右、 右子树:右
        bool isSame = left && right;                    // 左子树:中、 右子树:中(逻辑处理)
        return isSame;

    }
    bool isSameTree(TreeNode* p, TreeNode* q) {
        return compare(p, q);
    }
};

116. 填充每个节点的下一个右侧节点指针

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/

层序遍历好想一点,递归的方法还是有点绕的。

思路

注意题目提示内容,:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

基本上就是要求使用递归了,迭代的方式一定会用到栈或者队列。

递归

一想用递归怎么做呢,虽然层序遍历是最直观的,但是递归的方式确实不好想。

如图,假如当前操作的节点是cur:

在这里插入图片描述

最关键的点是可以通过上一层递归 搭出来的线,进行本次搭线。

图中cur节点为元素4,那么搭线的逻辑代码:(注意注释中操作1和操作2和图中的对应关系

if (cur->left) cur->left->next = cur->right; // 操作1
if (cur->right) {
    if (cur->next) cur->right->next = cur->next->left; // 操作2
    else cur->right->next = NULL;
}

理解到这里,使用前序遍历,那么不难写出如下代码:

class Solution {
private:
    void traversal(Node* cur) {
        if (cur == NULL) return;
                                // 中
        if (cur->left) cur->left->next = cur->right; // 操作1
        if (cur->right) {
            if (cur->next) cur->right->next = cur->next->left; // 操作2
            else cur->right->next = NULL;
        }
        traversal(cur->left);   // 左
        traversal(cur->right);  // 右
    }
public:
    Node* connect(Node* root) {
        traversal(root);
        return root;
    }
};

迭代(层序遍历)

本题使用层序遍历是最为直观的,如果对层序遍历不了解,看这篇:二叉树:层序遍历登场! (opens new window)。

遍历每一行的时候,如果不是最后一个Node,则指向下一个Node;如果是最后一个Node,则指向nullptr。

代码如下:

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        if (root != nullptr) que.push(root);
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                Node* node = que.front();
                que.pop();
                if (i != size - 1) {
                    node->next = que.front(); //如果不是最后一个Node 则指向下一个Node
                } else node->next = nullptr;  //如果是最后一个Node 则指向nullptr
                if (node->left != nullptr) que.push(node->left);
                if (node->right != nullptr) que.push(node->right);
            }
        }
        return root;
    }
};

还有一种方法,可以看出每一层的指向方向是从左向右,但是层序遍历也是从左向右,这样要判断下一个指向的节点还要判断是不是这一层最右边的node。如果从右向左进行层序遍历,则可有效的把“上一个”节点记录下来,转换为next指针的指向。

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        if(root != NULL){
            que.push(root);
        }
        while(!que.empty()){
            int size = que.size();
            Node* next = NULL;
            for(int i = 0; i < size; i++){
                Node* node = que.front();
                que.pop();
                node->next = next;
                next = node;
                if(node->right){
                    que.push(node->right);
                }
                if(node->left){
                    que.push(node->left);
                }
            }
        }
        return root;
    }
};

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

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

相关文章

Java 访问权限控制

使用访问权限控制的原因&#xff1a; 使用户不要触碰到那些不该触碰的部分类库设计者可以更改类的内部工作模式&#xff0c;而不必担心整体程序造成影响 访问权限修饰词 Java具有三种访问权限修饰词(public private protected)&#xff0c;划分出了四种访问权限(public prot…

WeakHashMap引起内存溢出,线程数剧增

1.问题背景 代码通过jarLoader的方式&#xff0c;实现了一个数据库多个客户端版本兼容的情况&#xff0c;一个客户端使用一个jarLoader&#xff0c;实现jar的隔离。 同时&#xff0c;jarLoader使用weekhashmap做缓存。 hive做批量查询表的元数据时&#xff0c;引入了线程池。…

NodeJS学习笔记一

文章目录1. 模块化1.1 模块作用域2. 内置API模块3. 自定义模块3.1 向外共享模块作用域中的成员3.2 使用误区3.3 CommonJS模块化规范4. 第三方API模块4.1 npm包管理目录4.2 包管理配置文件 package.json4.3 devDependencies节点4.4 切换npm的包镜像源4.5 包的分类4.5.1 项目包4.…

【cfeng-Work】work项目理解

work 项目 内容管理入职项目环境搭建商用项目 和 学习项目经验分享easily 分享work 见解 学习项目&#xff08;非商用&#xff09;相比工作中的商用项目差别还是很大的… Cfeng 最近开始工作&#xff0c; 接下来我将分享一下对于学校中的project环境和 work环境的相关理解 入职…

生成式对抗网络(GAN)原理推导与网络构建思路

0 引言 设想这样的场景&#xff1a;你是一个工作室的老板&#xff0c;你的工作室主要用来生产名画的赝品&#xff1b;而真正的名画则为前人所创造&#xff0c;存放在收藏室中。你的赝品画会和真品画一起被鉴定家鉴定&#xff0c;而你的终极目标是成为一个以假乱真的工艺大师。…

设置 Postman 环境并调用 API

云开发自带 API 调试 功能&#xff0c;可快速进行 API 接口调试&#xff0c;同时也支持通过 Postman 调用 API 获取设备信息或控制设备。本文介绍如何设置调用云开发 API 的 Postman 开发环境及调用接口实践。 配置环境 安装 Postman 8.2.2 及以上版本桌面软件。 版本过低会出…

Python类中的__init__() 和 self 的解析

1、Python中self的含义 self&#xff0c;英文单词意思很明显&#xff0c;表示自己&#xff0c;本身。 此处有几种潜在含义&#xff1a; 1.这里的自己&#xff0c;指的是&#xff0c;实例Instance本身。 2.同时&#xff0c; 由于说到“自己”这个词&#xff0c;都是和相对而…

《计算机网络》——第七章知识点

防火墙:特殊编程的路由器&#xff0c;实施访问控制策略。分为网络级防火墙(防止网络出现非法入侵)和应用级防火墙(进行应用的访问控制)。 本次文章详细PDF请点击以下链接查看&#xff1a; https://download.csdn.net/download/qq_53142796/87353770https://download.csdn.n…

市场回暖进行时,实体店商户们千万不要做这三件事!

随着防控措施的进一步优化&#xff0c;市场环境逐渐复苏&#xff0c;许多商户都趁着这段时间开始着手做准备&#xff0c;想要趁年前赚到消费回暖的第一波红利。 但是方向一旦错了&#xff0c;不管付出的努力有多少&#xff0c;最终也只能和想要的结果背道而驰&#xff1b;为了避…

图文结合带你搞懂MySQL日志之Slow Query Log(慢查询日志)

GreatSQL社区原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。GreatSQL是MySQL的国产分支版本&#xff0c;使用上与MySQL一致。作者&#xff1a;KAiTO文章来源&#xff1a;GreatSQL社区原创 什么是慢查询日志 MySQL 的慢查询日志&#xff0c;用来记录在 M…

ORB-SLAM2 --- Tracking::Track 追踪线程解析

1.函数作用 ORB-SLAM2的三大线程之一---跟踪线程&#xff0c;负责估计运动信息、跟踪局部地图。 追踪线程的主要工作原理就是我们从数据集中读入一帧帧&#xff0c;刚开始的时候跟踪线程没有进行初始化&#xff08;没有初始化不知道世界坐标系的原点和相机的位姿&#xff09;&a…

做开发的朋友说软件测试是个人都能学,我当面怒怼

那就分析一下测试工程师需要会哪些技能&#xff1a; 01 软件测试理论 作为一个软件测试人员&#xff0c;总不能不知道软件测试本身的东西吧。 你总要知道软件测试是什么&#xff1f;什么是测试用例&#xff1f;什么是缺陷&#xff1f;软件测试有些什么特点&#xff1f;这些…

OpenHarmony#深入浅出学习eTs#(八)“猜大小”小游戏

本项目Gitee仓地址&#xff1a;[深入浅出eTs学习: 带大家深入浅出学习eTs (gitee.com)]( 上一章节提到的模拟器存在的BUG问题&#xff0c;目前没有办法直接改善&#xff0c;本来打算直接使用鸿蒙远程设备来实现&#xff0c;但是发现支持API8的设备都被抢光了&#xff08;包括模…

如何使用谷歌地图和LightningChart创建地理空间地图图表?

LightningChart JS 是一款高性能的 JavaScript 图表工具&#xff0c;专注于性能密集型、实时可视化图表解决方案。 LightningChart .JS | 下载试用&#xff08;qun&#xff1a;740060302&#xff09;https://www.evget.com/product/4189/download Google Static Maps API 是一…

前端基础(二)_JavaScript变量、JavaScript标识符、JavaScript获取元素、JavaScript的鼠标事件

一、JavaScript变量 变量是存储数据的容器&#xff0c;例x10&#xff0c;则x中存储的值为10。 语法&#xff1a;var 变量名 值。 1.1、变量的声明 先声明后赋值 var x; // 声明变量x alert(x); // undefined 所有声明了但是没有赋值的变量&#xff0c;结果都为undefined X…

单商户商城系统功能拆解55—后台设置

单商户商城系统&#xff0c;也称为B2C自营电商模式单店商城系统。可以快速帮助个人、机构和企业搭建自己的私域交易线上商城。 单商户商城系统完美契合私域流量变现闭环交易使用。通常拥有丰富的营销玩法&#xff0c;例如拼团&#xff0c;秒杀&#xff0c;砍价&#xff0c;包邮…

水文监测系统 水文遥测终端机 遥测终端机-助力母亲河平稳度汛

平升电子水文监测系统/水文遥测终端机/遥测终端机实现对江河流域水位、降水量、流量、流速、水质、闸门开启度、墒情等数据的实时采集、报送和处理。为防汛抗旱减灾提供科学依据和有效信息共享&#xff0c;保障人民群众生命财产安全&#xff0c;满足水利和经济社会发展对水文服…

虚拟化平台安装并升级显卡驱动

前言 在虚拟化平台上&#xff0c;虚拟化要使用vGPU&#xff0c;需要同时在主机和虚拟机上安装显卡驱动&#xff0c;主机和虚拟机的显卡驱动需要保守一致。安装驱动时先安装主机驱动&#xff0c;再安装虚拟机驱动。 驱动下载 可以从显卡官网许可中心下载对应的虚拟化驱动&…

一篇文章教小白学会搭建 vite + ts + vue3 项目,手把手教程,不会算我输

目录 一、基础环境和工具 1. 使用 pnpm 代替 npm 2. 使用 pinia 代替 vuex 3. 使用 windicss 4. 使用 vscode 5. 使用 sourcetree 6. 了解 vite ts vue 二、项目搭建 1. 流程图 2. 初始化项目 2.1 使用命令初始化项目 2.2 项目结构 3. 使用 git 3.1 创建本地的…

【JavaSE成神之路】数组思考题讲解

哈喽&#xff0c;我是兔哥呀&#xff0c;今天就让我们继续这个JavaSE成神之路&#xff01; 这一节啊&#xff0c;咱们要学习的内容是数组的思考题讲解&#xff0c;也就是上一节我留下的两个小作业。 数组是非常重要的知识点&#xff0c;也是我们后续学习各种数据结构的基础&am…