Leetcode算法训练日记 | day20

一、合并二叉树

1.题目

Leetcode:第 617 题

给你两棵二叉树: root1 和 root2 。

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意: 合并过程必须从两个树的根节点开始。

示例 1:

输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]

示例 2:

输入:root1 = [1], root2 = [1,2]
输出:[2,2]

2.解题思路

首先检查两个输入树的根节点是否为空,如果其中一个为空,则返回另一个作为结果。如果两个根节点都不为空,将合并它们的值,并对它们的左右子树递归地执行合并操作。这个过程一直持续到所有节点都被合并,最终返回更新后的根节点,包含了合并后二叉树的根。

3.实现代码

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

// 一、合并两棵二叉树(递归法)。
class Solution1 {
public:
    // mergeTrees函数用于合并两个给定的二叉树root1和root2。
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (root1 == NULL) return root2;// 如果root1为空,则返回root2作为合并后的树的根节点。
        if (root2 == NULL) return root1; // 如果root2为空,则返回root1作为合并后的树的根节点。

        root1->val = root1->val + root2->val;// 将root1的值与root2的值相加,并将结果赋给root1,这是合并操作的一部分。
        root1->left = mergeTrees(root1->left, root2->left);// 递归调用mergeTrees函数合并root1和root2的左子树。
        root1->right = mergeTrees(root1->right, root2->right); // 递归调用mergeTrees函数合并root1和root2的右子树。

        return root1;// 返回更新后的root1作为合并后的树的根节点。
    }
};

// 二、合并两棵二叉树(迭代法法)。
class Solution2 {
public:
    // mergeTrees函数用于合并两个给定的二叉树root1和root2。
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (root1 == NULL) return root2;  // 如果root1为空,直接返回root2作为合并后的树的根节点。
        if (root2 == NULL) return root1; // 如果root2为空,直接返回root1作为合并后的树的根节点。

        queue<TreeNode*> que; // 创建一个队列que,用于在合并过程中存储待处理的节点对。
        que.push(root1); // 将root1和root2入队。
        que.push(root2);

        // 使用while循环处理队列中的所有节点对。
        while (!que.empty()) {
            // 取出队列中的两个节点,node1对应root1的节点,node2对应root2的节点。
            TreeNode* node1 = que.front();
            que.pop();
            TreeNode* node2 = que.front();
            que.pop();

            node1->val = node1->val + node2->val;// 将node1和node2的值相加,并将结果赋给node1。

            // 如果node1和node2都有左子节点,将它们入队以进行后续合并。
            if (node1->left != NULL && node2->left != NULL) {
                que.push(node1->left);
                que.push(node2->left);
            }

            // 如果node1和node2都有右子节点,将它们入队以进行后续合并。
            if (node1->right != NULL && node2->right != NULL) {
                que.push(node1->right);
                que.push(node2->right);
            }

            //因为返回的是root1二叉树,只需考虑考虑root1存在空节点对应的root2不为空节点的情况
            // 而不需要考虑root1存在节点对应的root2为空节点的情况
            // 如果node1没有左子节点,但node2有左子节点,将node2的左子节点赋给node1。
            if (node1->left == NULL && node2->left != NULL) {
                node1->left = node2->left;
            }

            // 如果node1没有右子节点,但node2有右子节点,将node2的右子节点赋给node1。
            if (node1->right == NULL && node2->right != NULL) {
                node1->right = node2->right;
            }//因为返回的是root1二叉树,所以不需要考虑root1存在节点对应的root2为空节点的情况
        }

        return root1;// 由于函数只接收root1的指针,返回root1,此时它已经是合并后的树的根节点。
    }
};

二、二叉搜索树中的搜索

1.题目

Leetcode:第 700 题

给定二叉搜索树(BST)的根节点 root 和一个整数值 val

你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

示例 1:

输入:root = [4,2,7,1,3], val = 2
输出:[2,1,3]

示例 2:

输入:root = [4,2,7,1,3], val = 5
输出:[]
2.解题思路

首先检查当前根节点是否为空或者是否已经找到目标值。如果当前节点为空,或者其值等于目标值,则直接返回当前节点。如果当前节点的值大于目标值,函数递归地在左子树中查找;如果当前节点的值小于目标值,则递归地在右子树中查找。最终,函数返回查找结果,如果找到了匹配的节点,则返回该节点;否则返回NULL。

3.实现代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

// 一、在二叉搜索树中查找特定值的节点(递归法)。
class Solution1 {
public:
    // searchBST函数用于在二叉搜索树root中查找值为val的节点。
    TreeNode* searchBST(TreeNode* root, int val) {
        if (root == NULL || root->val == val) return root; // 如果根节点为空,或者根节点的值等于要查找的值val,返回当前节点。
        TreeNode* result = NULL;// 初始化结果指针为NULL,用于存储找到的节点。
        if (root->val > val) result = searchBST(root->left, val);// 如果根节点的值大于要查找的值val,递归搜索左子树。
        if (root->val < val) result = searchBST(root->right, val);// 如果根节点的值小于要查找的值val,递归搜索右子树。   
        return result;// 返回搜索结果,如果找到则返回找到的节点,否则返回NULL。
    }
};


// 二、在二叉搜索树中查找特定值的节点(递归法)。
class Solution2 {
public:
    // searchBST函数用于在二叉搜索树root中查找值为val的节点并返回。
    TreeNode* searchBST(TreeNode* root, int val) {
        while (root != NULL) {  // 使用while循环,当root不为NULL时继续搜索。
            if (root->val > val) {// 如果root的值大于要查找的值val,向左子树搜索。
                root = root->left;
            }
            else if (root->val < val) {// 如果root的值小于要查找的值val,向右子树搜索。
                root = root->right;
            }
            else {// 如果root的值等于要查找的值val,找到了目标节点,返回root。
                return root;
            }
        }
        return NULL; //未找到就返回NULL。
    }
};

三、验证二叉搜索树

1.题目

Leetcode:第 98 题

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左

    子树

    只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:root = [2,1,3]
输出:true

示例 2:

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。
2.解题思路

1.一般递归法:递归遍历整个二叉树,将所有节点的元素使用vector保存,检查是否所有的元素都是严格递增的,如果不是,就说明不是一个有效的二叉搜索树。

2.双指针递归法:在递归遍历整个二叉树的过程中,用两个指针来检查是否满足每个节点的值都应该大于其左子树中所有节点的值,并且小于其右子树中所有节点的值。如果不是,就说明不是一个有效的二叉搜索树。

3.迭代法:通过使用栈 st 来存储待访问的节点,可以在每次循环中选择最左边的节点进行访问,从而模拟中序遍历的过程。同时,使用指针 pre 来记录遍历过程中的前一个节点值,以便在访问当前节点时检查其是否违反了二叉搜索树的性质。如果遍历完整棵树都没有发现违反性质的情况,则可以认为该二叉树是有效的二叉搜索树。

3.实现代码
#include <iostream>
#include <vector>
#include <stack>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

// 一、验证二叉搜索树(一般递归法)。
class Solution {
public:
    vector<int> vec; // 定义一个vec,用于存储二叉树节点的值

    // 定义一个名为traversal的成员函数,用于执行二叉树的中序遍历。
    void traversal(TreeNode* root) {
        if (root == NULL) return; // 如果当前节点为空,直接返回,不执行任何操作。
        traversal(root->left); // 递归调用traversal函数遍历当前节点的左子树。
        vec.push_back(root->val); // 访问当前节点,将其值添加到vec中。
        traversal(root->right); // 递归调用traversal函数遍历当前节点的右子树。
    }

    // 定义一个名为isValidBST的成员函数,用于验证给定的二叉树是否为有效的二叉搜索树。
    // 参数root是二叉树的根节点指针。
    bool isValidBST(TreeNode* root) {
        vec.clear(); // 清空向量vec,为新的中序遍历做准备。
        traversal(root); // 调用traversal函数,传入根节点,执行中序遍历。
        
        for (int i = 1; i < vec.size(); i++) {// 遍历向量vec,检查中序遍历的结果是否为升序。
            if (vec[i] <= vec[i - 1]) return false; // 如果发现任何违反升序的元素对,返回false。
        }
        return true; // 如果遍历完成没有发现违反升序的元素对,返回true,表明是有效的二叉搜索树。
    }
};

//二、验证二叉搜索树(双指针递归法)。
class Solution2 {
public:
    // 定义一个指向TreeNode的指针pre,初始化为NULL,用于在遍历过程中记录前一个访问的节点。
    TreeNode* pre = NULL;
    // 定义一个名为isValidBST的成员函数,用于验证给定的二叉树是否为有效的二叉搜索树。
    // 参数root是二叉树的根节点指针。
    bool isValidBST(TreeNode* root) {
        if (root == NULL) return true;  // 如果当前节点为空,说明是有效的二叉搜索树,返回true。

        // 递归调用isValidBST函数检查当前节点的左子树是否为有效的二叉搜索树。
        bool left = isValidBST(root->left);

        // 如果pre不为NULL,并且前一个访问的节点的值大于当前节点的值,说明不是有效的二叉搜索树,返回false。
        // 这是二叉搜索树的性质:每个节点的值都应该大于其左子树中所有节点的值。
        if (pre != NULL && pre->val > root->val) return false;

        pre = root; // 更新pre为当前节点,以便在递归检查右子树时使用。

        // 递归调用isValidBST函数检查当前节点的右子树是否为有效的二叉搜索树。
        bool right = isValidBST(root->right);

        // 返回左子树和右子树的检查结果,只有当左右子树都满足二叉搜索树的性质时,整个树才是有效的。
        return left && right;
    }
};

// 三、验证二叉搜索树(迭代法)。
class Solution3 {
public:
    // 定义一个成员函数isValidBST,用于验证给定的二叉树是否为有效的二叉搜索树。
    // 参数root是二叉树的根节点指针。
    bool isValidBST(TreeNode* root) {
        stack<TreeNode*> st; // 定义一个栈st,用于存放二叉树的节点指针,辅助进行迭代遍历。
        TreeNode* cur = root; // 定义一个当前节点指针cur,初始指向根节点。
        TreeNode* pre = NULL; // 定义一个前一个节点指针pre,初始为NULL,用于记录遍历过程中的前一个节点值。

        // 当当前节点不为空,或者栈st不为空时,继续遍历。
        while (cur != NULL || !st.empty()) {
            
            if (cur != NULL) {// 如果当前节点不为空,将当前节点压入栈st,并将cur更新为当前节点的左子节点。
                st.push(cur); // 将当前节点压入栈中。
                cur = cur->left; // 将cur更新为当前节点的左子节点,开始遍历左子树。
            }
            else {
                // 如果当前节点为空,从栈st中弹出一个节点作为当前节点。
                cur = st.top(); // 获取栈顶节点。
                st.pop(); // 弹出栈顶节点。

                // 如果pre不为空,并且当前节点的值小于pre记录的前一个节点的值,说明不是有效的二叉搜索树,返回false。
                // 这是二叉搜索树的性质:每个节点的值都应该大于其左子树中所有节点的值。
                if (pre != NULL && cur->val <= pre->val) {
                    return false;
                }
                pre = cur;// 更新pre为当前节点。
                cur = cur->right;// 将cur更新为当前节点的右子节点,准备遍历右子树。
            }
        }
        // 如果遍历完整棵树都没有发现违反二叉搜索树性质的情况,则返回true,表明是有效的二叉搜索树。
        return true;
    }
};

ps:以上皆是本人在探索算法旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ARM Coresight SOC-600 -- ETF Flushin无法接收到 CTI 发出 triggerout 信号问题分析】

请阅读【嵌入式开发必备专栏 】 文章目录 问题背景波形分析问题背景 在做验证的时候,准备通过 CTI2 给 SOC 上的 ETF 触发一个 flushin 动作,然后stop住 formatter,结果一致发现没有成功,接下来就是分析的过程了。 首先检查了代码,没有发现代码有什么问题(一般自己写的代…

2000-2022年县域常住人口和户籍人口数据

2000-2022年县域常住人口和户籍人口数据/县常住人口及县户籍人口数据 1、时间&#xff1a;2000-2022年 2、来源&#xff1a;县域统计年鉴、各省年鉴 3、指标&#xff1a;户籍人口数、常住人口数 4、范围&#xff1a;县区级&#xff0c;具体县名单参看数据预览 5、缺失情况…

C语言—每日选择题—Day69

第一题 1、以下程序的输出结果是&#xff08; &#xff09; int main() {char arr[2][4];strcpy (arr[0],"you");strcpy (arr[1],"me");arr[0][3]&;printf("%s \n",arr);return 0; } A: you&me B: you C: me D: err 答案及解析 A 这里重…

2024年4月12日 十二生肖 今日运势

小运播报&#xff1a;2024年4月12日&#xff0c;星期五&#xff0c;农历三月初四 &#xff08;甲辰年戊辰月丙午日&#xff09;&#xff0c;法定工作日。 红榜生肖&#xff1a;羊、狗、虎 需要注意&#xff1a;牛、马、鼠 喜神方位&#xff1a;西南方 财神方位&#xff1a;…

2024 年最新前端工程师使用 Webpack 模块打包工具详细教程(更新中)

概述 Webpack 模块打包工具 Webpack 是一个现代的静态模块打包工具&#xff0c;用于将前端应用程序的各种资源&#xff08;例如如&#xff1a;JavaScript、CSS、图片等&#xff09;视为模块&#xff0c;并将它们打包成可以在浏览器中运行的静态文件。它的主要功能包括模块打包…

蓝桥杯-【二分】分巧克力,跳石头

代码及解析: #include<bits/stdc.h> using namespace std; int n,k; const int N100010; int h[N],w[N]; bool check(int d){int num0;for(int i0;i<n;i) num (h[i]/d)*(w[i]/d);if(num>k) return true; //够分else return false; //不够分 } in…

Java代码基础算法练习-统计学生成绩-2024.04.11

任务描述&#xff1a; 编写程序&#xff0c;输入n个(0<n<50)学生的成绩(输入-1结束)&#xff0c;要求统计并输出优秀(大任务描述:于85)、及格(60~84)和不及格(小于60)的学生人数。(成绩取值范围0~100) 任务要求&#xff1a; 代码示例&#xff1a; /*** 这个程序用于统计…

《看漫画学C++》第12章 可大可小的“容器”——向量

在C编程的世界里&#xff0c;数组是一种基础且广泛使用的数据结构。然而&#xff0c;传统的静态数组在大小固定、管理不便等方面的局限性&#xff0c;常常让开发者感到束手束脚。幸运的是&#xff0c;C标准库中的vector类为我们提供了一种更加灵活、高效的动态数组解决方案。 …

Qt for MCUs 2.7正式发布

本文翻译自&#xff1a;Qt for MCUs 2.7 released 原文作者&#xff1a;Qt Group高级产品经理Yoann Lopes 翻译&#xff1a;Macsen Wang Qt for MCUs的新版本已发布&#xff0c;为Qt Quick Ultralite引擎带来了新功能&#xff0c;增加了更多MCU平台的支持&#xff0c;并且我们…

容错组合导航

在初始值正确的情况下&#xff0c;惯性导航短期精度较高&#xff0c;但是其误差随着时间是累计的。如果要提高惯性导航的长期精度&#xff0c;就必须提高惯性器件的精度和初始读准精度&#xff0c;这必将大大提高成本。 如果将惯性导航与其他导航系统适当地组合起来&#xff0c…

为什么在深度神经网络中,网络权重的初始化很重要?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 在深度神经网络中&#xff0c;网络权重的初始化非常关键&#xff0c;因为它对网络的训练速度、收敛能力以及最终的性能都有重大影响。具体来说&#xff0c;权重初始化的重要性主要体现在以下几个方面&a…

【从浅学到熟知Linux】冯诺依曼体系结构及进程概念详谈!

&#x1f3e0;关于专栏&#xff1a;Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 冯诺依曼体系结构操作系统如何理解管理操作系统概念设计操作系统目的系统调用和库函数概念 进程基本概念描…

探索设计模式的魅力:MVVM模式在AI大模型领域的创新应用-打破传统,迎接智能未来

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 MVVM模式在AI大模型领域的创新应用-打破传统迎接智能未来 &#x1f680; “在人工智能的领域里&a…

炒股自动化:交易接口API才是重点,券商官方散户可用的接口

上一篇我们用get_full_tick取到了数据&#xff0c;也讲了变量和字典的基本概念&#xff0c;这次我们向交易所发送订单试试。前面文章的链接放在文末了&#xff0c;需要的可以看一下 这些内容是给新手看的&#xff0c;找接口的大佬们直接拉到文末即可 取市场数据的方法很多&am…

Python中Python-docx 包的run介绍

先对run做一个简单地介绍。每个paragraph对象都包含一个run对象的列表。举例&#xff1a; 这是一个简短的段落。 from docx import Document doc Document("1.docx") #上面这段话保存在1.docx中 print("这一段的run个数是&#xff1a;",len(doc.paragr…

国家统计局行政区划获取及入库ES实践

我们先看下最终效果&#xff1a; 1. ES索引新建 PUT administrative_division {"mappings": {"properties": {"province": {"type": "keyword"},"province_code": {"type": "keyword"},&q…

HarmonyOS 开发-阻塞事件冒泡

介绍 本示例主要介绍在点击事件中&#xff0c;子组件enabled属性设置为false的时候&#xff0c;如何解决点击子组件模块区域会触发父组件的点击事件问题&#xff1b;以及触摸事件中当子组件触发触摸事件的时候&#xff0c;父组件如果设置触摸事件的话&#xff0c;如何解决父组…

最近一些前端面试问题整理

最近一些前端面试问题整理 4月8号1. TS 中的 类型别名 和接口的区别是什么&#xff1f;2. 什么是深拷贝和浅拷贝&#xff1f;深浅拷贝的方法有哪些&#xff1f;浅拷贝&#xff08;Shallow Copy&#xff09;深拷贝&#xff08;Deep Copy&#xff09;区别总结 3. 使用 JSON.strin…

wsl 2在windows11上的设置

详细参考&#xff1a;Manual installation steps for older versions of WSL | Microsoft Learn 1.系统组件要打开 分别是&#xff1a;Hyper-V、虚拟机平台、适用于Windows的Linux子系统 2.以管理员方式运行命令行&#xff0c;逐步执行下面的命令 update to WSL 2, you must…

【MATLAB源码-第185期】基于matlab的16QAM系统相位偏移估计EOS算法仿真,对比补偿前后的星座图误码率。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. 引言 M-QAM调制技术的重要性 现代通信系统追求的是更高的数据传输速率和更有效的频谱利用率。M-QAM调制技术&#xff0c;作为一种高效的调制方案&#xff0c;能够通过在相同的带宽条件下传输更多的数据位来满足这一需求…