算法设计与分析 树形dp

简介: 算法设计与分析 树形dp

树形dp

概述

  • 使用前提:如果题目求解的目标是S规则,则求解的流程可以定成以每一个节点为头节点的子树在S规则下的每一个答案,并且最终答案一定在其中
  • 套路步骤:
    (1)以某一个节点X为头节点的子树中,分析答案有哪些可能性(难点),并且这种分析是以X的左子树,X的右子树和X的整个树的角度来考虑可能性的
    (2)根据(1)的可能性分析,列出所有需要的信息
    (3)合并(2)的信息,对左右树提出同样的要求,并写出信息结构
    (4)设计递归函数,递归函数是处理以X为头节点的情况下的答案,包括设计递归的basecase,默认直接得到左右树的所有信息(黑盒的构造),以及把可能性做整合,并且要返回第三步的信息结构(黑盒的拆解)(在以下题目中,递归函数设计部分对黑盒的使用用更为详细的说明)

题目一:二叉树节点间的最大距离问题

image.png

image.png

如图:a到b的距离为2,d到c的距离为4,d到k的距离为6

  • 枚举(暴力)思路:任意两个节点直接均可以求出直接的距离,所有可以求出任意两节点距离后找最大值,但是时间复杂度过高
  • 树形dp思路:
    1 可能性分析:两大类,X是否参与最大值
    (1)X参与,X左树的最低端到X右树的最低端,左树的高度 + 1 + 右树的高度
    (2)X不参与,左右树内部解决,左树的最大距离,右树的最大距离
    (3)X的最大长度就在:左树的最大距离;右树的最大距离;左树的高度 + 1 + 右树的高度,三者中的最大值
    2 信息要求
    (1)情况一:左树的高度,右树的高度
    (2)情况二:左树的最大距离,右树的最大距离
    (3)综上所述:子树需要给父节点返回的信息结构:最大距离,高度
    3 递归函数设计
    (1)basecase:节点为空时返回信息(0,0)
    (2)黑盒的构造:假设直接得到了左右子树想要的信息结构
    (3)拆解黑盒:给出具体一个节点在得到左右子树信息后,如何整合出该节点的信息结构
    public static class Node{
        //树结点
        public int value;
        public Node left;
        public Node right;
        public Node(int date){
            //初始化
            this.value = date;
        }
    }
    public static class Info{
        //信息返回类型
        public int maxDistance;
        public int height;
        public Info(int maxDistance, int height){
            this.maxDistance = maxDistance;
            this.height = height;
        }
    }
    public static int maxDistance(Node head){
        //最大距离函数
        return process(head).maxDistance;
    }
    public static Info process(Node x){
        //求以X为头的整棵树的Info信息
        if (x == null){
          //basecase
            return new Info(0, 0);
        }
        //黑盒的构造:递归调用得到左右子树的Info信息
        Info leftInfo = process(x.left);
        Info rightInfo = process(x.right);
        //信息的整合
        //不含X时左右树提供的最大距离
        int leftDistance = leftInfo.maxDistance;
        int rightDistance = rightInfo.maxDistance;
        //含X时的最大距离
        int xDistance = leftInfo.height + 1 + rightInfo.height;
        //拆解黑盒:给出该节点信息的解决方法
        int maxDistance = Math.max(xDistance, Math.max(leftDistance, rightDistance));
        int height = Math.max(leftInfo.height, rightInfo.height) + 1;
        return new Info(maxDistance, height);
    }

题目二:排队最大快乐值(多叉树问题)

image.png

image.png

  • 树形dp思路:
    1 可能性分析:两大类,X是否参与最大值
    (1)X参与,最大值:X的值 + X子树的顶点不参与的情况下,提供的最大值
    (2)X不参与,最大值:0 + max(子树顶点参与,子树顶点不参与)
    (3)最大值:X参与,X不参与的最大值
    2 信息要求
    (1)情况一:头节点参与的最大值
    (2)情况二:头节点不参与的最大值
    (3)综上所述:子树需要给父节点返回的信息结构:(子树顶点参与,子树顶点不参与)
    3 递归函数设计
    (1)basecase:节点为叶子节点时,返回(该节点的值,0)
    (2)黑盒的构造:假设直接得到了所有子树想要的信息结构
    (3)拆解黑盒:给出具体一个节点在得到所有子树信息后,如何整合出该节点的信息结构
    public static class Employee{
        //员工信息类
        public int happy; //快乐值要求达到最大
        public List<Employee> nexts; //子节点
        public Employee(int date){
            //初始化
            this.happy = date;
        }
    }
    public static class Info{
        //信息返回类型
        public int xMaxHappy; //x节点参与
        public int maxHappy; //x不参与
        public Info(int xMaxHappy, int maxHappy){
            this.xMaxHappy = xMaxHappy;
            this.maxHappy = maxHappy;
        }
    }
    public static int maxHappy(Employee boss){
        //最大快乐值
        Info headInfo = process(boss);
        return Math.min(headInfo.xMaxHappy, headInfo.maxHappy);
    }
    public static Info process(Employee x){
        //返回x的Info信息
        if (x.nexts.isEmpty()){
            //basecase
            return new Info(x.happy, 0);
        }
        int xMaxHappy = x.happy;
        int maxHappy = 0;
        for (Employee next : x.nexts){
            //遍历x的子树
            //黑盒的构造
            Info nextInfo = process(next);
            //拆黑盒
            xMaxHappy += nextInfo.maxHappy;
            maxHappy += Math.max(nextInfo.xMaxHappy, nextInfo.maxHappy);
        }
        return new Info(xMaxHappy, maxHappy);
    }

题目三:判断是否为满二叉树

  • 树形dp思路:
    1 可能性分析:深度H,节点数为N
    (1)是满二叉树:N == 2 ^ H - 1
    (2)不是满二叉树:N != 2 ^H - 1
    2 信息要求
    (1)父节点的H = 左右子树中H的较大值 + 1
    (2)父节点的N = 左N + 右N
    (3)子树需要给父节点返回的信息结构:深度,节点树
    3 递归函数设计
    (1)basecase:节点为空,返回(0,0)
    (2)黑盒的构造:假设直接得到了左右子树想要的信息结构
    (3)拆解黑盒:给出具体一个节点在得到所有子树信息后,如何整合出该节点的信息结构
      public static class Node{
        int value;
        Node left;
        Node right;
        public Node(int date){
            this.value = date;
        }
    }
    public static class Info{
        public int height; //树的高度
        public int num; //节点数
        public Info(int height, int num){
            //初始化
            this.height = height;
            this.num = num;
        }
    }
    public static boolean isFull(Node head){
        //判断是否为满二叉树
        Info headInfo = process(head);
        //1 << x, 2的x次方
        return headInfo.num == (1 << headInfo.height - 1);
    }
    public static Info process(Node x){
      //返回x的Info信息
        if (x == null){
            //basecase
            return new Info(0, 0);
        }
        //黑盒子的构建
        Info left = process(x.left);
        Info right= process(x.right);
        //拆解黑盒
        int height = Math.max(left.height, right.height) + 1;
        int num = left.num + right.num + 1;
        return new Info(height, num);
    }

题目四:判断是否为平衡二叉树

  • 树形dp思路:
    1 可能性分析:
    (1)X平衡:左右均为平衡二叉树,且|左高度 - 右高度| <= 1
    (2)X不平衡,不满足平衡的一条就行
    2 信息要求
    (1)左右子树是否平衡
    (2)左右树的高度
    (3)子树需要给父节点返回的信息结构:是否平衡,高度
    3 递归函数设计
    (1)basecase:节点为空,返回(true,0)
    (2)黑盒的构造:假设直接得到了左右子树想要的信息结构
    (3)拆解黑盒:给出具体一个节点在得到所有子树信息后,如何整合出该节点的信息结构
    public static class Node{
        int value;
        Node left;
        Node right;
        public Node(int date){
            this.value = date;
        }
    }
    public static class Info {
        //Info
        public boolean isBalanced; //树是否平衡
        public int height; 
        public Info(boolean isBalanced, int height){
            //构造函数
            this.isBalanced = isBalanced;
            this.height = height;
        }
    }
    public static boolean isBalancedTree(Node head){
        return process(head).isBalanced;
    }
    public static Info process(Node x){
      //返回x的Info信息
        if (x == null){
            //basecase
            return new Info(true, 0);
        }
        //黑盒构造
        Info leftTree = process(x.left); 
        Info rightTree= process(x.right);
        //拆解黑盒
        int height = Math.max(leftTree.height, rightTree.height) + 1;
        boolean isBalanced = leftTree.isBalanced && rightTree.isBalanced
                && Math.abs(leftTree.height - rightTree.height) < 2;
        return new Info(isBalanced, height);
    }

题目五:判断是否为二叉排序树

  • 树形dp思路:
    1 可能性分析:
    (1)X是:左max < head.value < 右min,左右均为二叉排序树
    (2)X不是,不满足条件
    2 信息要求
    (1)左右子树是否为二叉排序树
    (2)左的max,min;右的min,max
    (1)子树需要给父节点返回的信息结构:是否为二叉排序树,左右的max,min
    3 递归函数设计
    (1)basecase:节点为空,因为max,min不好设置,直接返回null
    (2)黑盒的构造:假设直接得到了左右子树想要的信息结构
    (3)拆解黑盒:给出具体一个节点在得到所有子树信息后,如何整合出该节点的信息结构
    public static class Node{
        int value;
        Node left;
        Node right;
        public Node(int date){
            this.value = date;
        }
    }
    public static class Info {
        //info
        public boolean isBST; //树是否为搜索树
        public int min; //树的最小值
        public int max; //树的最大值
        public Info(boolean isBST, int min, int max){
            //构造函数
            this.isBST = isBST;
            this.min = min;
            this.max = max;
        }
    }
    public static boolean isBSTree(Node head){
        return process(head).isBST;
    }
    public static Info process(Node x){
        //返回x的Info
        if (x == null){
            //空树的min,max不好设置,直接设置为null
            return null;
        }
        //黑盒构造
        Info leftTree = process(x.left); //递归调用
        Info rightTree= process(x.right);
        //拆解黑盒
        int min = x.value; //min,max的设置
        int max = x.value;
        if (leftTree != null){
            //左子树为空
            min = Math.min(min, leftTree.min);
            max = Math.max(max, leftTree.max);
        }
        if (rightTree != null){
            //右子树为空
            min = Math.min(min, rightTree.min);
            max = Math.max(max, rightTree.max);
        }
        boolean isBST = true; //isBST设置
        if (leftTree != null && (leftTree.isBST || leftTree.max > x.value)){
            //左不为空的条件下:左子树不是搜索二叉树或左的max > head,value
            //不满足搜索二叉树
            isBST = false;
        }
        if (rightTree != null && (rightTree.isBST || rightTree.min < x.value)){
            isBST = false;
        }
        return new Info(isBST, min, max);
    }


目录
相关文章
|
23天前
|
人工智能 算法 BI
第一周算法设计与分析 D : 两面包夹芝士
这篇文章介绍了解决算法问题"两面包夹芝士"的方法,通过找出两个数组中的最大最小值,计算这两个值之间的整数个数,包括特判不存在整数的情况。
|
15天前
|
算法 数据可视化
基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
奇异谱分析(SSA)是一种基于奇异值分解(SVD)和轨迹矩阵的非线性、非参数时间序列分析方法,适用于提取趋势、周期性和噪声成分。本项目使用MATLAB 2022a版本实现从强干扰序列中提取趋势线,并通过可视化展示了原时间序列与提取的趋势分量。代码实现了滑动窗口下的奇异值分解和分组重构,适用于非线性和非平稳时间序列分析。此方法在气候变化、金融市场和生物医学信号处理等领域有广泛应用。
|
1天前
|
机器学习/深度学习 存储 人工智能
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
使用Python作为开发语言,基于文本数据集(一个积极的xls文本格式和一个消极的xls文本格式文件),使用Word2vec对文本进行处理。通过支持向量机SVM算法训练情绪分类模型。实现对文本消极情感和文本积极情感的识别。并基于Django框架开发网页平台实现对用户的可视化操作和数据存储。
11 0
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
|
23天前
|
算法
算法设计与分析作业
这篇文章是关于算法设计与分析的作业,其中包含了两个算法实现:一个是使用分治算法实现的十进制大整数相乘(包括加法、减法和乘法函数),并进行了正确性和健壮性测试;另一个是使用快速排序思想实现的分治查找第K小元素的程序,并分析了其平均和最坏时间复杂度。
算法设计与分析作业
|
19天前
|
编解码 算法 图形学
同一路RTSP|RTMP流如何同时回调YUV和RGB数据实现渲染和算法分析
我们播放RTSP|RTMP流,如果需要同时做渲染和算法分析的话,特别是渲染在上层实现(比如Unity),算法是python这种情况,拉两路流,更耗费带宽和性能,拉一路流,同时回调YUV和RGB数据也可以,但是更灵活的是本文提到的按需转算法期望的RGB数据,然后做算法处理
|
23天前
|
人工智能 算法
第一周算法设计与分析:C : 200和整数对之间的情缘
这篇文章介绍了解决算法问题"200和整数对之间的情缘"的方法,通过统计数组中每个数模200的余数,并计算每个同余类中数的组合数来找出所有满足条件的整数对(i, j),使得\( A_i - A_j \)是200的整数倍。
|
23天前
|
人工智能 算法
第一周算法设计与分析 G : 排队援救
这篇文章介绍了解决算法问题"排队援救"的方法,通过使用队列和映射来模拟救援点的排队过程,并确定最终得到救援的人的顺序和编号。
|
23天前
|
算法 C++
第一周算法设计与分析 E : 构造回文串
这篇文章介绍了解决算法问题"构造回文串"的方法,即判断给定的整数N(视为字符串)是否可以通过在前面添加任意个0(或不添加)来构造一个回文串,并给出了相应的C++代码实现。
|
23天前
|
算法 C++
第一周算法设计与分析 H : 括号匹配
这篇文章介绍了解决算法问题"括号匹配"的方法,通过使用栈来检查给定字符串中的括号是否合法匹配,并提供了相应的C++代码实现。
|
23天前
|
算法 C++
第一周算法设计与分析 F : 模拟计算器
该文章 "第一周算法设计与分析 F : 模拟计算器" 的摘要或讨论。这篇文章介绍了如何设计一个程序来模拟一个基本的计算器,处理包含加、减、乘运算的表达式,并给出了相应的C++代码实现