关于树形dp问题的解决

news2024/9/30 9:24:28

文章目录

    • 解决套路
    • 案例展示
      • 一、二叉树的最大深度
      • 二、判断是不是平衡二叉树
      • 三、判断是不是二叉搜索树
      • 四、判断是否是满二叉树
      • 五、二叉树节点间的最大距离
      • 六、派对的最大快乐值

解决套路

实际上就是设计一个递归函数,该递归函数一定要包含 basecase即让函数趋于终止的条件

处理节点 node 时,我们默认直接能够获得左子树和右子树的相关信息,然后将所有可能性进行整合。

往往是通过 node 的左子树,node 的右子树,包含 node 的整棵树来分析可能性,从而根据题目列出需要的信息

对节点 node 是这样要求的,同样对于右子树和左子树也要提出相同的要求,进行递归

案例展示

二叉树节点类

class TreeNode {
  int val = 0;
  TreeNode left = null;
  TreeNode right = null;
  public TreeNode(int val) {
    this.val = val;
  }
}

一、二叉树的最大深度

在这里插入图片描述

basecase:

如果 root 为 null,即为空节点,那么返回的高度为 0

递归设计:

默认获取到了 root 节点的左子树高度和右子树高度

节点 root 的高度一定等于其左子树的高度和右子树的高度的最大值,然后在此基础上加上 1,表示到了 root 节点层,高度增加了 1

public class Solution {
    public int maxDepth (TreeNode root) {
        if(root == null) {
            return 0;//basecase
        } 
        return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
    }
}

二、判断是不是平衡二叉树

平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

在案例一的基础上,我们可以选择直接使用 IsBalanced_Solution() 方法来求得节点 root 是否是平衡二叉树,通过 maxDepth() 方法来求得节点 root 的左子树和右子树的高度。如果高度差的绝对值超过 1,那么直接歇菜,返回 false;如果没有超过 1,那么就再次使用 IsBalanced_Solution() 方法来验证 root.left 和 root.right 是否是平衡二叉树

这样的方法可行,但是会出现很多的重复计算树的高度的行为,所以可以选择通过一个类来记录信息,信息包括这棵树是否为平衡二叉树,以及该树的高度是多少

basecase:

如果节点 root 为 null,即空节点,那么它一定是一颗平衡二叉树,并且高度为 0

递归设计:

默认已经获取到了左子树和右子树的高度,也知晓左右子树是否都是平衡二叉树

那么以 root 为头结点的树成为平衡二叉树有三个条件:

①左子树是平衡树 ②右子树是平衡树 ③左右子树的高度差的绝对值不超过 1

求得该子树是否为平衡二叉树后,需要求得该子树的高度,其高度就是左右子树高度的最大值加一,为 root 节点的父节点提供信息(如果此时的 root 节点并不是题目提供的树的头结点的话)

public class Solution {
    class Demo2 {
        public boolean isBLC;//该子树是否是平衡二叉树
        public int height;//该子树的高度
        public Demo2(boolean isBLC,int height) {
            this.isBLC = isBLC;
            this.height = height;
        }
    }
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null) {
            return true;
        }
        return func(root).isBLC;
    }
    public Demo2 func(TreeNode root) {
        if(root == null) {
            return new Demo2(true,0);//basecase
        }
        Demo2 demo1 = func(root.left);//获取左子树信息
        Demo2 demo2 = func(root.right);//获取右子树信息

        //判断当前子树是否为平衡二叉树的三个条件
        boolean isBLC = demo1.isBLC && demo2.isBLC
                && (Math.abs(demo1.height-demo2.height) <= 1);

        //求得当前子树的高度
        int height = Math.max(demo1.height,demo2.height) + 1;
        return new Demo2(isBLC,height);
    }
}

三、判断是不是二叉搜索树

二叉搜索树满足每个节点的左子树上的所有节点均小于当前节点且右子树上的所有节点均大于当前节点。

根据二叉搜索树的这个特点,如果我们对该二叉树进行中序遍历,那么遍历得到的结果一定是升序的,所以我们可以通过中序遍历来判断该树是否为二叉搜索树

当然也可以使用树形dp套路来解决,通过一个类去记录某一棵树是否为二叉搜索树,这棵树的最大值以及最小值

basecase:

如果 root 节点为 null,即空节点,那就返回 null,表示没有该子树

递归设计:

默认已经获取到了左右子树是否为二叉搜索树信息,以及左右子树分别的最大值和最小值

根据搜索二叉树的特点,对于当前子树的头结点 root 来说,其左子树的最大值一定比 root 节点的值小,其右子树的最小值一定比 root 节点的值大

所以判断当前树是否为平衡二叉树的条件:

① 如果左子树存在,左子树一定是二叉搜索树,并且左子树的最大值一定比 root 节点的值小,否则歇菜

② 如果右子树存在,右子树一定是二叉搜索树,并且右子树的最小值一定比 root 节点的值大,否则歇菜

最后还要根据左子树、右子树的最大最小值以及 root 的值来更新当前树的最大值和最小值

public class Solution1 {
    public class returnType {
        public boolean isBST;//当前树是否是二叉搜索树
        public int max;//树的最大值
        public int min;//数的最小值
        public returnType(boolean isBST,int ma,int mi) {
            this.isBST = isBST;
            this.max = ma;
            this.min = mi;
        }
    }
    public boolean isValidBST (TreeNode root) {
        return func(root).isBST;
    }
    public returnType func(TreeNode root) {
        if(root == null) {
            return null;//该子树啥也没有
        }
        //获取到左右子树的结果
        returnType left = func(root.left);
        returnType right = func(root.right);
        //更新最大值和最小值
        int min = root.val;
        int max = root.val;
        if(left != null) {
            //左子树是有节点的
            min = Math.min(min,left.min);
            max = Math.max(max,left.max);
        }
        if(right != null) {
            //右子树是有节点的
            min = Math.min(min,right.min);
            max = Math.max(max,right.max);
        }
        boolean isBST = false;
        //成为二叉搜索树的条件
        if((left != null?(left.isBST && left.max < root.val) : true ) &&
                (right != null?(right.isBST && right.min > root.val) : true)) {
            isBST = true;
        }
        return new returnType(isBST,max,min);
    }
}

四、判断是否是满二叉树

满二叉树就是除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树

在这里插入图片描述

那么根据满二叉树的性质,我们可以发现规律:满二叉树的节点数量等于 2树的高度 -1

我们使用一个类来记录当前树的高度以及当前树的节点个数

basecase:

如果 root 为 null,即 root 为空节点,那么树的高度为 0,树的节点个数自然也为 0

递归设计:

默认知晓 root 节点的左子树的高度和节点个数,右子树的高度和节点个数

那么当前以 root 为头结点的树的高度自然就是其左右子树高度的最大值加一,树的节点个数自然就是左右子树的节点个数之和加一

public class Solution {
    class Demo {
        public int height;//子树高度
        public int count;//子树节点个数
        public Demo(int height,int count) {
            this.height = height;
            this.count = count;
        }
    }
    public boolean isFull(TreeNode root) {
        if(root == null) {
            return true;
        }
        Demo demo = func(root);
        //判断是否为满二叉树
        return 1 << demo.height - 1 == demo.count;
    }
    public Demo func(TreeNode root) {
        if (root == null) {
            return new Demo(0,0);
        }
        Demo demo1 = func(root.left);
        Demo demo2 = func(root.right);
        int height = Math.max(demo1.height,demo2.height) + 1;
        int count = demo1.count + demo2.count + 1;
        return new Demo(height,count);
    }
}

五、二叉树节点间的最大距离

从二叉树的某个节点 X 触发,可以向上走,也可以像下走,但是途经的节点不可重复经过,到达另一个节点 Y 时路径上的节点个数被认为是节点 X 到节点 Y 的距离(包含节点 X、Y),那么二叉树任何两个节点之间都会有距离,求整个树上的最大距离

在这里插入图片描述

如上图所示,该树的最大距离就是 7

通过 Info 这个类来记录信息,包含当前树的最大距离以及该树的高度

basecase:

如果 root 为 null,即为空节点,那么该树的最大距离和树的高度自然都为 0

递归设计:

默认知道 root 节点的左子树的最大距离以及高度,以及右子树的最大距离以及高度

如果 root 节点参与到最大距离的路径中了,所能够获得的最大距离就应该是左右子树高度之和再加一。

如果 root 节点没有参与到最大距离的路径中,那么获得的最大距离一定在左子树或者右子树中产生。

三者取最大即为该树的最大距离

public class Solution {
    class Info {
        public int distance;//该树能够获得的最大距离
        public int height;//该树的高度
        public Info (int distance,int height) {
            this.distance = distance;
            this.height = height;
        }
    }
    public int getDistance (TreeNode root) {
        return func(root).distance;
    }
    public Info func(TreeNode root) {
        if (root == null) {
            return new Info(0,0);
        }
        Info left = func(root.left);
        Info right = func(root.right);
        int num1 = left.distance;//左子树的最大距离(X不参与)
        int num2 = right.distance;//右子树的最大距离(X不参与)
        int num3 = left.height + right.height + 1;//X参与得到的最大距离
        int distance = Math.max(num1,Math.max(num2,num3));//三者取最大
        int height = Math.max(left.height, right.height)+1;//该树的高度
        return new Info(distance,height);
    }
}

六、派对的最大快乐值

现有员工信息可以通过一个类来定义

class Employee {
    public int happy;//该员工的快乐值
    List<Employee> subordinates;//该员工的直接下属们
}

公司的每个员工都符合 Employee 类的描述,所以可以将公司的人员结构看成是一颗多叉树,头结点自然就是大 boss,除了老板外,所有员工都有自己的唯一上级,除了基层员工(叶子节点)外,都有属于自己的一个或多个直接下属,基层员工的 subordinates 列表为null

现在需要办一个派对,员工可以来也可以不来,但是有一个硬性规定,员工甲来了,那么甲的直接下属就一定不能来。派对的快乐值就是参加派对的员工的快乐值的累加

求如何让派对的快乐值最大?返回最大值

通过 HappyInfo 这个类来记录员工 x 去派对所获得的最大快乐值以及不去派对所获得的最大快乐值

basecase:

如果员工 x 是基层员工,那么他是没有直系下属的,他去了,最大快乐值就是他自己的快乐值,不去就没有快乐值

递归设计:

默认已经知晓员工 x 所有直系下属去或者不去派对所能够获得的最大快乐值

如果员工 x 去了派对,那么他的直系下属一定去不了,我们就将他直系下属的去不了派对所获得的最大快乐值进行累加,再加上员工 x 的快乐值,从而得到员工 x 去派对所能够获得的最大快乐值

如果员工 x 不去派对,那么他的直系下属可以去,可以不去,两者取较大值进行累加操作,得到员工 x 不去派对所能够获得的最大快乐值

public class Solution {
    class HappyInfo {
        public int go;//去派对获得的最大快乐值
        public int notToGo;//不去派对获得的最大快乐值
        public HappyInfo (int go,int notToGo) {
            this.go = go;
            this.notToGo = notToGo;
        }
    }
    public int Party(Employee x) {
        HappyInfo happyInfo = getHappiness(x);//x 是大boss
        //取大 boss 去或者不去所获得的快乐值的较大值
        return Math.max(happyInfo.go,happyInfo.notToGo);
    }
    public HappyInfo getHappiness (Employee x) {
        if (x.subordinates == null) {
            return new HappyInfo(x.happy,0);//基层员工
        }
        int go = x.happy;//x 员工去派对得到的最大快乐
        int notToGo = 0;//x 员工不去派对得到的最大快乐
        
        //遍历一下 x 的所有直系下属
        for (Employee e:x.subordinates) {
            HappyInfo happyInfo = getHappiness(e);
            go += happyInfo.notToGo;
            //x 员工去了,直系下属一定不去
            notToGo += Math.max(happyInfo.go,happyInfo.notToGo);
            //x 没去,直系下属去或者不去取较大值
        }
        return new HappyInfo(go,notToGo);
    }
}

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

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

相关文章

[附源码]Python计算机毕业设计高校选课系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等…

[附源码]Python计算机毕业设计共享自习室管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等…

【C++】STL-string的使用

文章目录1.为什么学习string类&#xff1f;1.1 C语言中的字符串OJ题目1&#xff1a;字符串相加2.标准库中的string类2.1 string类(了解)总结&#xff1a;2.2 string类的常用接口说明1. string类对象的常见构造2.string类对象的容量操作max_sizereserve和resize3. string类对象的…

Spirng 痛苦源码学习(三)——Spring中的几个核心老大

文章目录前言一、基础接口1.Resource2.ResourceLoader3.BeanFactory&#xff08;1&#xff09;总览&#xff08;2&#xff09;继承如下&#xff08;3&#xff09;重要的工厂4. ApplicationContext前言 一开始学习spring我们首先还是要搞清楚他的整体架构&#xff0c;就是他是干…

[附源码]Node.js计算机毕业设计互联网教学平台Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

C++ Reference: Standard C++ Library reference: Containers: map: multimap: end

C官网参考链接&#xff1a;https://cplusplus.com/reference/map/multimap/end/ 公有成员函数 <map> std::multimap::end C98 iterator end(); const_iterator end() const; C11 iterator end() noexcept; const_iterator end() const noexcept;返回指向结束的iterator …

HTTP简介(GET,POST)

HTTP简介(GET,POST) 简介 HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。 演示 HTTP协议的特点 基于TCP协议: 面向连接&#xff0c;安全 TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于…

[附源码]Python计算机毕业设计SSM基于Java家庭财务管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

LeetCode 297. 二叉树的序列化与反序列化

今天早上睡起来刷了这么一道题&#xff0c;二叉树的序列化和反序列化 大概意思就是给你一个二叉树&#xff0c;把他转成一个字符串&#xff0c;中间的自定义规则由你定&#xff0c;再根据这个字符串去还原这个二叉树&#xff0c;这道题的话思路不难&#xff0c;写起来有的细节…

【云原生 | Kubernetes 实战】13、K8s 常见的存储方案及具体应用场景分析(上)

目录 K8s 持久化存储 一、k8s 持久化存储&#xff1a;emptyDir 二、k8s 持久化存储&#xff1a;hostPath 三、k8s 持久化存储&#xff1a;nfs 3.1 搭建 nfs 服务 3.2 创建Pod&#xff0c;挂载 NFS 共享出来的目录 3.3 测试 pod 挂载 nfs 是否成功 K8s 持久化存储 在 k8s…

[附源码]Nodejs计算机毕业设计基于框架的在线问答平台Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

[附源码]Python计算机毕业设计公租房管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

ASPICE详细介绍-1.什么是ASPICE?

什么是ASPICE&#xff1f; ASPICE全称是“Automotive Software Process Improvement and Capacity dEtermination”&#xff0c;即汽车软件过程改进及能力评定&#xff0c;简称A-SPICE或ASPICE。 属于过程模型&#xff0c;由过程和能力度两个维度构成&#xff0c;用于评价汽车…

DBeaver连接hive(详细图解)

文章目录DBeaver 简介使用DBeaver连接hive&#xff08;1&#xff09;配置Maven仓库&#xff08;2&#xff09;配置hive驱动&#xff08;3&#xff09;创建hive连接&#xff08;4&#xff09;连接测试DBeaver 简介 dbeaver是免费和开源&#xff08;GPL&#xff09;为开发人员和数…

RK3588平台开发系列讲解(AUDIO篇)Linux音频调试--alsa-utils 工具

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、工具介绍二、工具的使用2.1 aplay2.2 arecord2.3 amixer2.4 aplay | arecord沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍Linux下audio调试工具alsa-utils的使用方法。 一、工具介绍 RK平台…

[ 数据结构 -- 手撕排序算法第一篇 ] 插入排序

文章目录前言一、常见的排序算法二、插入排序的实现2.1 基本思想2.2 直接插入排序2.2.1 单趟排序的代码实现三、插入排序代码实现四、插入排序测试五、插入排序的时间复杂度5.1 最坏的情况下5.2 最好的情况下六、直接插入排序特性总结总结前言 手撕排序算法第一篇&#xff1a;…

HashSet源码解析

HashSet源码解析 问题 &#xff08;1&#xff09;集合&#xff08;Collection&#xff09;和集合&#xff08;Set&#xff09;有什么区别&#xff1f; &#xff08;2&#xff09;HashSet 怎么保证添加元素不重复&#xff1f; &#xff08;3&#xff09;HashSet 是否允许 nu…

基于nodejs大学生宿舍管理系统的设计与实现.zip(论文+源码+ppt文档+视频录制)

相关资料下载地址&#xff1a;请点击下载》》》 1 绪论 3 1.1 项目开发背景和意义 3 1.2 项目研究现状 3 1.3 项目主要的内容 4 2 相关技术介绍及环境开发条件 4 2.1相关技术介绍 4 2.1.1 nodejs开发框架 4 2.1.2 MySQL数据库 5 2.1.3VSCODE开发工具 6 2.2环境开发条件 6 3 系…

C51——震动控制喇叭

通过继电器使得喇叭稍微的带一点点智能的感觉&#xff1a; 当振动传感器模块 感受到震动的时候让喇叭响起来&#xff0c;这喇叭之后两根线来供电&#xff0c;这便少不了继电器的作用 震动模块&#xff0c;继电器和单片机的连接方式在前面已经提到了 这里我们先让电源的负极和…

就是收藏,不解释。Jmeter接口测试数据库断言的实现与设计

接口测试大部分都使用接口的响应值作为接口验证的依据&#xff0c;但在与数据库有交互的接口中这种结果判断不足以判断接口的正确性&#xff0c;本文将以jmeter作为接口测试的工具来实现接口的数据库断言 一、总体思路概述 使用jmeter发起接口请求使用json提取器获取接口响应结…