算法设计与分析 树形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);
    }


目录
相关文章
|
2月前
|
机器学习/深度学习 算法 搜索推荐
从理论到实践,Python算法复杂度分析一站式教程,助你轻松驾驭大数据挑战!
【10月更文挑战第4天】在大数据时代,算法效率至关重要。本文从理论入手,介绍时间复杂度和空间复杂度两个核心概念,并通过冒泡排序和快速排序的Python实现详细分析其复杂度。冒泡排序的时间复杂度为O(n^2),空间复杂度为O(1);快速排序平均时间复杂度为O(n log n),空间复杂度为O(log n)。文章还介绍了算法选择、分而治之及空间换时间等优化策略,帮助你在大数据挑战中游刃有余。
76 4
|
17天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
46 1
|
1月前
|
算法
树的遍历算法有哪些?
不同的遍历算法适用于不同的应用场景。深度优先搜索常用于搜索、路径查找等问题;广度优先搜索则在图的最短路径、层次相关的问题中较为常用;而二叉搜索树的遍历在数据排序、查找等方面有重要应用。
33 2
|
2月前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
2月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
29 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
2月前
|
算法
PID算法原理分析
【10月更文挑战第12天】PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。
|
2月前
|
算法
PID算法原理分析及优化
【10月更文挑战第6天】PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。
|
2月前
|
存储 算法
动态规划算法学习一:DP的重要知识点、矩阵连乘算法
这篇文章是关于动态规划算法中矩阵连乘问题的详解,包括问题描述、最优子结构、重叠子问题、递归方法、备忘录方法和动态规划算法设计的步骤。
140 0
|
2月前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
31 0