LeetCode——二叉树

2023-11-16

二叉树概念和性质

参考博客

104.二叉树的的最大深度(递归)

给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
在这里插入图片描述
思路 :利用递归,深度优先搜索,调用递归函数看结点的左子树和右子树中哪个更深,直到遇到叶子结点
官方题解
在这里插入图片描述
代码

/**
 * 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:
    int maxDepth(TreeNode* root) {
        //深度优先搜索
        if(!root)
          return 0;
        return max(maxDepth(root->left),maxDepth(root->right))+1;
    }
};

98. 验证二叉搜索树(中序遍历)

给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。
    在这里插入图片描述
    解法一 用一个数组存放经过中序遍历的结点值
  • 由于要求所有的左子树小于当前结点所有右子树大于当前结点,因此可以利用中序遍历,将二叉树线性化,得到一个序列,后面只需判断这个序列是否严格升序就好了(不能有相同元素)
  • 时间空间都是O(N)

代码

/**
 * 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:
    bool isValidBST(TreeNode* root) {
    	//中序遍历得到的序列
        vector<int>treeNum;
        //对二叉树进行中序遍历
        InOrderTraverse(root,treeNum);
        //判断是否升序
        for(int i=1;i<treeNum.size();i++)
        {
            if(treeNum[i]<=treeNum[i-1])
                return false;
        }
        return true;
    }
    //中序遍历(左根右)
    void InOrderTraverse(TreeNode * root,vector<int>&arr)
    {
        if(root)
        {
        	//递归调用
            InOrderTraverse(root->left,arr);
            arr.push_back(root->val);
            InOrderTraverse(root->right,arr);
        }
    }
};

解法二 在中序遍历过程中直接判断是否有序(来自题解【代码随想录】)

  • 不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了。
  • 要定义一个long long的全局变量,用来比较遍历的节点是否有序,因为后台测试数据中有int最小值,所以定义为long long的类型,初始化为long long最小值。
  • 终止条件:节点为空时,返回true
  • 单层递归逻辑:中序遍历,一直更新maxVal,一旦发现maxVal >= root->val,就返回false,注意元素相同时候也要返回false。

代码

class Solution {
public:
    long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值
    bool isValidBST(TreeNode* root) {
        if (root == NULL) return true;

        bool left = isValidBST(root->left);
        // 中序遍历,验证遍历的元素是不是从小到大
        if (maxVal < root->val) maxVal = root->val;
        else return false;
        bool right = isValidBST(root->right);

        return left && right;
    }
};

101. 对称二叉树(代码比较精巧,不好理解)

给定一个二叉树,检查它是否是镜像对称的。
在这里插入图片描述
注意:

上图中下面的那个示例,说明即使数字是对称的,但是所在的位置不是镜像对称的也不是对称二叉树,所以不能只考虑比较结点值,而应注意是否为对称位,因此不能验证通过中序遍历得到的数组是否是回文串来判断是否为对称二叉树。

思路: (官方题解)

  • 利用一个递归函数check,通过同步移动两个指针的方法来遍历这棵树
  • p指针和q指针一开始都指向根节点,随后p往左子树跳时,q往右子树跳;p往右子树跳时,q往左子树跳。
  • 每次跳都检查当前p和q的结点值是否相等,以及是否对称(当有一个为空而另外一个不为空时说明不对称)。

代码

class Solution {
public:
    bool check(TreeNode *p, TreeNode *q) {
    	//都为空时返回true
        if (!p && !q) return true;
        //只有一个为空时,说明不对称,返回false
        if (!p || !q) return false;
        //比较当前结点值,以及利用递归遍历整棵树
        return p->val == q->val && check(p->left, q->right) && check(p->right, q->left);
    }

    bool isSymmetric(TreeNode* root) {
        return check(root, root);
    }
};

102. 二叉树的层序遍历(中等,参考题解,自己码的代码)

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
在这里插入图片描述
思路:

  • 由于需要逐层遍历结点,所以想到要用广度优先遍历,利用队列实现(之前学数据结构的时候学到的,还好还记得)
  • 但是本题不能用普通的广度优先遍历得到一个一维数组,而是要得到一个根据层序排列的二维数组,这里我参考了官方题解的做法,就是:
    • 首先根元素入队
    • 当队列不为空的时候
    1. 求当前队列的长度 size
    2. 依次从队列中取 size个元素进行拓展(将结点的左子树和右子树压到队尾,如果有的话),然后进入下一层的迭代
  • 也就是说,每次进行拓展的size个元素是属于同一层的,将这些数用一维数组存起来,每层拓展完成后将这个数组加到二维数组中去。

代码

/**
 * 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:
    vector<vector<int>> levelOrder(TreeNode* root) {
       if(!root) return{};
       vector<vector<int>>result;
       vector<int>tmp;

       TreeNode* node;
       //利用队列
       queue<TreeNode*>que;
       //放入根结点
       que.push(root);
       while(!que.empty())
       {
           //求出每层的节点数
           int queSize=que.size();
           //展开que中queSize个元素(属于同一层的元素)
           for(int i=0;i<queSize;i++)
           {
             //取出队列中的第一个元素
             node=que.front();
             //然后将它弹出
             que.pop();
             //将节点值插入数组
             tmp.push_back(node->val);
             
             //展开当前节点的坐左子树和右子树(如果有的话)
             //将左孩子和右孩子节点压到队列尾部
             if(node->left)
             que.push(node->left);

             if(node->right)
             que.push(node->right);
            }
        //向结果数组压入二叉树每层的节点值
        result.push_back(tmp);
        tmp.clear();
       }
       return result;
    }
};

108. 将有序数组转换为二叉搜索树(递归)

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
在这里插入图片描述
思路:

  • 主要原理是将一个中序数组构造成搜索二叉树,且满足高度平衡。
  • 利用一个递归函数实现,每次递归都将数组中间位置的值作为根节点(如果数组长为偶数,那就取中间靠左的那个)。
  • 新的根节点需要创建在堆区,否则不能返回它的地址。
  • 左半段数组同样是一个中序数组,继续递归,作为左子树,右半段数组同。
  • 关键是如何截取数组

代码

/**
 * 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* sortedArrayToBST(vector<int>& nums)
    {
        //借助一个递归函数来实现
        return helper(nums,0,nums.size()-1);
    }
    TreeNode * helper(vector<int>& nums,int left,int right)
    {
    	//终止条件
        if(left>right)
        return nullptr;
        //中序序列构造搜索二叉树
        //每次取数组中点作为根节点
        int mid=(left+right)/2;
        //递归调用,创建一个新节点,左子树为数组左半段,右子树为数组右半段
        TreeNode* root=new TreeNode(nums[mid],helper(nums,left,mid-1),helper(nums,mid+1,right));
        //返回根节点地址(开辟在堆区)
        return root;
    }
};

剑指 Offer 04. 二维数组中的查找(二叉搜索树)

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
在这里插入图片描述
思路:

  • 矩阵matrix有非常特殊的排列方式,从上到下递增,从左到右递增。
  • 那么对于最左下和最右上的元素,可将其分别作为根节点,将矩阵看做一个二叉搜索树15左边的元素更小,右边元素更大;18上边元素更小,右边元素更大。
    在这里插入图片描述
  • 将矩阵上建立直角坐标系,假如从18那个位置(坐标原点)开始搜索,假如小于目标target=5,则将搜索位置向右移动,大于则向上移动
    在这里插入图片描述
    代码
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
		//注意异常判断
        if(matrix.empty())
        return 0;
        int m=matrix.size(),n=matrix[0].size();
        //从最左下开始搜索
        int i=m-1,j=0;
        while(i>=0&&j<n)
        {
            if(target<matrix[i][j])
            i--; //向上
            else if(target>matrix[i][j])
            j++; //向右
            else
            return true;
        }
        return false;
    }

剑指 Offer 07. 重建二叉树(递归,二叉树遍历)

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例一

思路

  • 先序遍历的第一个结点为根,在中序遍历中确定根节点的位置
  • 根据中序遍历的特点,中序根节点前的为左子树,后边的为右子树
  • 由此可以得到先序遍历中的左子树和右子树,然后递归,再从左右子树中确定根节点以及他们的左右子树
    思路
  • 注意:递归实现中,不要忘了终止条件!!!
  • 左右子树的边界要仔细想想

代码

/**
 * 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* mybuild(vector<int>& preorder, vector<int>& inorder,unordered_map<int,int>& index,int pre_left,int pre_right,int in_left,int in_right)
    {
        //不要忘了递归终止条件!!!
        if(pre_left>pre_right)
        return NULL;

        //先序遍历中的根节点
        int pre_root=preorder[pre_left];

        //构建根节点
        TreeNode* root=new TreeNode(pre_root);

        //根据先序根节点找中序遍历根节点
        int in_root=index[pre_root];
        //根据中序根节点计算左子树长度
        int left_size=in_root-in_left;

        //构建左子树,最后四个参数分别为先序和中序遍历中的左子树部分
        root->left=mybuild(preorder,inorder,index,pre_left+1,pre_left+left_size,in_left,in_root-1);

        //构建右子树,后四个参数分别为先序和中序遍历中的右子树部分
        root->right=mybuild(preorder,inorder,index,pre_left+left_size+1,pre_right,in_root+1,in_right);

        return root;

    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n=preorder.size();
        unordered_map<int,int>index;
        for(int i=0;i<inorder.size();i++)
        {
            index[inorder[i]]=i;
        }

        TreeNode* head=mybuild(preorder,inorder,index,0,n-1,0,n-1);

        return head;
    }
};

617. 合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

思路:

  • 递归,深度优先
  • 如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,用一个新节点作为合并后的节点,而不是修改原来的节点值
  • 如果有一个为空,则将另外一个不为空的节点作为新二叉树的节点
  • 如果两个节点都为空,就返回空NULL

代码

/**
 * 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* mergeTrees(TreeNode* root1, TreeNode* root2) {
        //如果两节点均非空
        if(root1!=NULL&&root2!=NULL)
        {
            int m_val=root1->val+root2->val;
            //构造一个新节点,值为重复节点的值的和
            TreeNode* newRoot=new TreeNode(m_val);
            //递归构造左右子树
            newRoot->left=mergeTrees(root1->left,root2->left);
            newRoot->right=mergeTrees(root1->right,root2->right);
            return newRoot;
        }
        //有一个为空
        else if(root1==NULL)
        {
            return root2;
        }
        else if(root2==NULL)
        {
            return root1;
        }
        //都为空
        return NULL;
    }
};

复杂度

  • 时间O(min(m,n))mn分别是两个二叉树的节点数。对两个二叉树同时进行深度优先搜索,只有当两个二叉树中的对应节点都不为空时才会对该节点进行显性合并操作,因此被访问到的节点数不会超过较小的二叉树的节点数。
  • 空间O(min(m,n))。空间复杂度取决于递归调用的层数,递归调用的层数不会超过较小的二叉树的最大高度,最坏情况下,二叉树的高度等于节点数。

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

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点(完全二叉树)。
填充它的每个next指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next指针设置为 NULL
初始状态下,所有 next 指针都被设置为NULL

进阶
你只能使用常量级额外空间
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
在这里插入图片描述
解法一

  • 层次遍历,用辅助队列空间O(N)
  • 层次遍历基于广度优先搜索,它与广度优先搜索的不同之处在于,广度优先搜索每次只会取出一个节点来拓展,而层次遍历会每次将队列中的所有元素都拿出来拓展,这样能保证每次从队列中拿出来遍历的元素都是属于同一层的
  • 注意进行异常输入判断,检查输入是否为空指针
class Solution {
public:
    Node* connect(Node* root) {
        //广度优先搜索,层次遍历
        queue<Node*>Q;
        int n=0;
        if(root==NULL)
        return root;
        Q.push(root);
        while(!Q.empty())
        {
            int size=Q.size();
            for(int i=0;i<size;i++)
            {
                Node* tmp=Q.front();
                Q.pop();
                if(i!=size-1)
                tmp->next=Q.front();
                if(tmp->left!=NULL)
                Q.push(tmp->left);
                if(tmp->right!=NULL)
                Q.push(tmp->right);
            }
            n++;
        }
        return root;
    }
};

解法二

  • 使用已建立的next 指针迭代,思路见官方题解
  • 空间O(1)

解法三

  • 思路同解法二,只是用的递归实现
    在这里插入图片描述
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LeetCode——二叉树 的相关文章

随机推荐

  • HashMap多线程造成了CPU100%,死循环

    resize 方法的时候是罪魁祸首
  • STM32F1xx IAP跳转App 后中断异常及解决

    网上看到一些网友遇到STM3F1xx系列编写IAP程序经常遇到跳转到App后中断异常的问题 如一触发串口接收中断就复位等 现梳理如下 其实引起上述异常的根本原因就是 共用一组件 中断无入口 如 IAP程序中配置并打开了USART1接收中断
  • 使用Vue实现div上下收缩动画效果

    封装组件
  • springboot原理

    1 什么是SpringBoot SpringBoot是一个快速开发框架 快速的将一些常用的第三方依赖整合 原理 通过Maven子父工程的方式 简化XML配置 全部采用注解形式 内置Http服务器 Jetty和Tomcat 最终以java应用
  • ConstrainLayout解决复杂的嵌套布局

    最近 项目比较忙 没什么时间写博客 今天我要讲的是 项目中复杂的嵌套布局你讲采取何种方式呢 如果按常规方式去做 估计你做完一个界面 估计够呛 我将推荐你们使用谷歌推出的ConstrainLayout 虽然还没有出正式版本 但用于复杂嵌套布局
  • OJ系统刷题 第九篇(难篇)

    13441 求小数的某一位 难题 二刷 三刷 时间限制 1 秒 内存限制 128 MB 分数 tfrac a b ba 化为小数后 小数点后第n位的数字是多少 输入 三个正整数a b n 相邻两个数之间用单个空格隔开 0
  • Windows下好用的终端程序ConEmu

    Windows下的终端程序一直是一个问题 默认的cmd已经老旧不堪 而且在Windows 10中已经取消了在此处打开终端的右键菜单 改为使用功能更加强大的Powershell 而Powershell虽然功能强大 但是默认自带的终端程序却很不
  • COCO数据集解析生成语义分割mask

    COCO数据集解析生成语义分割mask 通过coco数据集的标注文件 instances train2014 json instances val2014 json 生成语义分割mask存在不同类别区域重叠问题 导致重叠部分像素的数值超出
  • linux的PAM认证和shadow文件中密码的加密方式

    它是一种统一的认证方案 PAM 让您能随时改变您的认证方法以及需求 并且不需要重新编译任何代码就封装了所有本地认证方法 具体见 PAM 网站 对于 PAM 您只需要做 对您的密码采用不同于 DES 的加密方式 让它们面对暴力解码 brute
  • “自顶向下,逐步求精“的方法简介

    自顶向下 逐步求精 的方法思路代表了生活中大多数事情的处理方法 它的奥妙之处在于将繁杂棘手的事务进行分解 逐部列条 化为最简易单调的子任务然后进行求解 如图即是一个很典型的逐步分解的问题模型 对于一件既定的事务 先进行总体性的了解即定出整个
  • halcon 中 select_shape 算子 相关特征参数

    求Region指定特征值 region features Regions Features Value 根据特征值选择区域 select shape Regions SelectedRegions Features Operation Mi
  • 安卓初学——界面按钮响应

    安卓学习 采用onClickListener监听器 界面按钮响应 一 定义监听 绑定组件 二 通过匿名内部类 把组件和事件绑定 三 采用view 对象调用onClick 四 在当前Activity实现监听接口 一 定义监听 绑定组件 自定义
  • VMware安装后打开就蓝屏

    VMware虚拟机开机蓝屏 追风 80 人赞同了该文章 目录 收起 一 查看主板上的虚拟化技术支持是否开启 二 开启虚拟机平台 如果在新建的虚拟机安装好后一点开机出现蓝屏 反复重装并且确定了新建虚拟机没有出错的情况下考虑是否是虚拟化没有开启
  • MobaXterm的下载安装

    下载地址 MobaXterm Xserver with SSH telnet RDP VNC and X11 Home Edition 进入页面后 点击绿色的方框下载 下载后得到一个压缩包 解压后可以看到有两个文件 点击 msi进行安装 选
  • Json Object转Model, Model、DataTable转Json Object (Jayrock技巧)

    本文假定读者有一定的Ext 控件的使用经验 看过Ext EditGridPanel实现效果的朋友会很惊讶 一个Grid就能实现所有增删改查功能 在展示给客户看时 让你的表现得很风骚 而他们又怎么知道 我们在调试js时 是多么痛苦 如何在js
  • PyTorch基础练习-task5(PyTorch实现L1,L2正则化以及Dropout)

    PyTorch基础练习 task5 一 Dropout原理 二 用代码实现正则化 L1和L2 2 1 L1实现 2 2 L2实现 三 PyTorch中实现dropout 一 Dropout原理 在前向传播的时候 让某个神经元的激活值以一定的
  • android edittext 输入完成监听,EditText输入监听

    EditText输入监听 原创 6710766562015 05 13 13 34 38著作权 文章分类 android开发 阅读数 548 著作权归作者所有 来自51CTO博客作者671076656的原创作品 如需转载 请注明出处 否则将
  • 关于idea 生成war 包放入tomcat的路径访问问题

    目录 1 打包成war 2 关于war 和war exploded 3 在idea中使用tomcat启动 4 把war包放在指定的tomcat下启动 1 打包成war 点击右上角project structure或者左上角File proj
  • leetcode刷题(不邻接植花、电话号码的字母组合、统计共同度过的日子数、节点与其祖先之间的最大差值、分隔数组以得到最大和、二进制求和、x的平方根、最小偶倍数)

    目录 1 不邻接植花 2 电话号码的字母组合 3 统计共同度过的日子数 4 节点与其祖先之间的最大差值 5 分隔数组以得到最大和 6 二进制求和 7 x的平方根 8 最小偶倍数 1 不邻接植花 2 电话号码的字母组合 class Solut
  • LeetCode——二叉树

    二叉树 二叉树概念和性质 104 二叉树的的最大深度 递归 98 验证二叉搜索树 中序遍历 101 对称二叉树 代码比较精巧 不好理解 102 二叉树的层序遍历 中等 参考题解 自己码的代码 108 将有序数组转换为二叉搜索树 递归 剑指