class078 树型dp-上【算法】

简介: class078 树型dp-上【算法】

class078 树型dp-上【算法】

算法讲解078【必备】树型dp-上

code1 333. 最大 BST 子树

// 最大BST子树

// 给定一个二叉树,找到其中最大的二叉搜索树(BST)子树,并返回该子树的大小

// 其中,最大指的是子树节点数最多的

// 二叉搜索树(BST)中的所有节点都具备以下属性:

// 左子树的值小于其父(根)节点的值

// 右子树的值大于其父(根)节点的值

// 注意:子树必须包含其所有后代

// 测试链接 : https://leetcode.cn/problems/largest-bst-subtree/

树形dp:

x头整树的maxBstSize

不含x:max(左树maxBstSize,右树maxBstSize)

包含x:左树isBst,右树isBst,左树max,右树min,如果max<x<min,返回:左树maxBstSize+右树maxBstSize+1

所以需要返回值(maxBstSize,isBst,max,min)

空树返回[0,true,MAX,MIN]

类似于后序遍历:得到孩子的返回值,再整合自己的返回值,最后返回给上层

package class078;
// 最大BST子树
// 给定一个二叉树,找到其中最大的二叉搜索树(BST)子树,并返回该子树的大小
// 其中,最大指的是子树节点数最多的
// 二叉搜索树(BST)中的所有节点都具备以下属性:
// 左子树的值小于其父(根)节点的值
// 右子树的值大于其父(根)节点的值
// 注意:子树必须包含其所有后代
// 测试链接 : https://leetcode.cn/problems/largest-bst-subtree/
public class Code01_LargestBstSubtree {
  // 不要提交这个类
  public static class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
  }
  // 提交如下的方法
  public static int largestBSTSubtree(TreeNode root) {
    return f(root).maxBstSize;
  }
  public static class Info {
    public long max;
    public long min;
    public boolean isBst;
    public int maxBstSize;
    public Info(long a, long b, boolean c, int d) {
      max = a;
      min = b;
      isBst = c;
      maxBstSize = d;
    }
  }
  public static Info f(TreeNode x) {
    if (x == null) {
      return new Info(Long.MIN_VALUE, Long.MAX_VALUE, true, 0);
    }
    Info infol = f(x.left);
    Info infor = f(x.right);
    // 左 4信息
    // 右 4信息
    // x 整合出4信息返回
    long max = Math.max(x.val, Math.max(infol.max, infor.max));
    long min = Math.min(x.val, Math.min(infol.min, infor.min));
    boolean isBst = infol.isBst && infor.isBst && infol.max < x.val && x.val < infor.min;
    int maxBSTSize;
    if (isBst) {
      maxBSTSize = infol.maxBstSize + infor.maxBstSize + 1;
    } else {
      maxBSTSize = Math.max(infol.maxBstSize, infor.maxBstSize);
    }
    return new Info(max, min, isBst, maxBSTSize);
  }
}

code2 1373. 二叉搜索子树的最大键值和

// 二叉搜索子树的最大键值和

// 给你一棵以 root 为根的二叉树

// 请你返回 任意 二叉搜索子树的最大键值和

// 二叉搜索树的定义如下:

// 任意节点的左子树中的键值都 小于 此节点的键值

// 任意节点的右子树中的键值都 大于 此节点的键值

// 任意节点的左子树和右子树都是二叉搜索树

// 测试链接 : https://leetcode.cn/problems/maximum-sum-bst-in-binary-tree/

x头整树的maxBstSum

不含x:max(左maxBstSum,右maxBstSum)

包含x:左isBst,右isBst,左max,右min,如果max<x<min返回左sum+右sum+x.val

整合(max,min,sum,isBst,maxBstSum)

空树返回(MAX,MIN,0,true,0)

package class078;
// 二叉搜索子树的最大键值和
// 给你一棵以 root 为根的二叉树
// 请你返回 任意 二叉搜索子树的最大键值和
// 二叉搜索树的定义如下:
// 任意节点的左子树中的键值都 小于 此节点的键值
// 任意节点的右子树中的键值都 大于 此节点的键值
// 任意节点的左子树和右子树都是二叉搜索树
// 测试链接 : https://leetcode.cn/problems/maximum-sum-bst-in-binary-tree/
public class Code02_MaximumSumBst {
  // 不要提交这个类
  public static class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
  }
  // 提交如下的方法
  public static int maxSumBST(TreeNode root) {
    return f(root).maxBstSum;
  }
  public static class Info {
    // 为什么这里的max和min是int类型?
    // 因为题目的数据量规定,
    // 节点值在[-4 * 10^4,4 * 10^4]范围
    // 所以int类型的最小值和最大值就够用了
    // 不需要用long类型
    public int max;
    public int min;
    public int sum;
    public boolean isBst;
    public int maxBstSum;
    public Info(int a, int b, int c, boolean d, int e) {
      max = a;
      min = b;
      sum = c;
      isBst = d;
      maxBstSum = e;
    }
  }
  public static Info f(TreeNode x) {
    if (x == null) {
      return new Info(Integer.MIN_VALUE, Integer.MAX_VALUE, 0, true, 0);
    }
    Info infol = f(x.left);
    Info infor = f(x.right);
    int max = Math.max(x.val, Math.max(infol.max, infor.max));
    int min = Math.min(x.val, Math.min(infol.min, infor.min));
    int sum = infol.sum + infor.sum + x.val;
    boolean isBst = infol.isBst && infor.isBst && infol.max < x.val && x.val < infor.min;
    int maxBstSum = Math.max(infol.maxBstSum, infor.maxBstSum);
    if (isBst) {
      maxBstSum = Math.max(maxBstSum, sum);
    }
    return new Info(max, min, sum, isBst, maxBstSum);
  }
}

code3 543. 二叉树的直径

// 二叉树的直径

// 给你一棵二叉树的根节点,返回该树的直径

// 二叉树的 直径 是指树中任意两个节点之间最长路径的长度

// 这条路径可能经过也可能不经过根节点 root

// 两节点之间路径的 长度 由它们之间边数表示

// 测试链接 : https://leetcode.cn/problems/diameter-of-binary-tree/

x头的直径

不含x:max(左直径,右直径)

包含x:左最大深度+右最大深度(不加1,边比点数量少1)

返回(直径,高度)

空树返回(0,0)

package class078;
// 二叉树的直径
// 给你一棵二叉树的根节点,返回该树的直径
// 二叉树的 直径 是指树中任意两个节点之间最长路径的长度
// 这条路径可能经过也可能不经过根节点 root
// 两节点之间路径的 长度 由它们之间边数表示
// 测试链接 : https://leetcode.cn/problems/diameter-of-binary-tree/
public class Code03_DiameterOfBinaryTree {
  // 不要提交这个类
  public static class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
  }
  // 提交如下的方法
  public static int diameterOfBinaryTree(TreeNode root) {
    return f(root).diameter;
  }
  public static class Info {
    public int diameter;
    public int height;
    public Info(int a, int b) {
      diameter = a;
      height = b;
    }
  }
  public static Info f(TreeNode x) {
    if (x == null) {
      return new Info(0, 0);
    }
    Info leftInfo = f(x.left);
    Info rightInfo = f(x.right);
    int height = Math.max(leftInfo.height, rightInfo.height) + 1;
    int diameter = Math.max(leftInfo.diameter, rightInfo.diameter);
    diameter = Math.max(diameter, leftInfo.height + rightInfo.height);
    return new Info(diameter, height);
  }
}

code4 979. 在二叉树中分配硬币

// 在二叉树中分配硬币

// 给你一个有 n 个结点的二叉树的根结点 root

// 其中树中每个结点 node 都对应有 node.val 枚硬币

// 整棵树上一共有 n 枚硬币

// 在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点

// 移动可以是从父结点到子结点,或者从子结点移动到父结点

// 返回使每个结点上 只有 一枚硬币所需的 最少 移动次数

// 测试链接 : https://leetcode.cn/problems/distribute-coins-in-binary-tree/

以x为头的树,几部

左步数+右步数+|左结点树-左硬币数|+|右结点树-右硬币数|

返回(步数,结点和,硬币和)

package class078;
// 在二叉树中分配硬币
// 给你一个有 n 个结点的二叉树的根结点 root
// 其中树中每个结点 node 都对应有 node.val 枚硬币
// 整棵树上一共有 n 枚硬币
// 在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点
// 移动可以是从父结点到子结点,或者从子结点移动到父结点
// 返回使每个结点上 只有 一枚硬币所需的 最少 移动次数
// 测试链接 : https://leetcode.cn/problems/distribute-coins-in-binary-tree/
public class Code04_DistributeCoins {
  // 不要提交这个类
  public static class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
  }
  // 提交如下的方法
  public static int distributeCoins(TreeNode root) {
    return f(root).move;
  }
  public static class Info {
    public int cnt;
    public int sum;
    public int move;
    public Info(int a, int b, int c) {
      cnt = a;
      sum = b;
      move = c;
    }
  }
  public static Info f(TreeNode x) {
    if (x == null) {
      return new Info(0, 0, 0);
    }
    Info infol = f(x.left);
    Info infor = f(x.right);
    int cnts = infol.cnt + infor.cnt + 1;
    int sums = infol.sum + infor.sum + x.val;
    int moves = infol.move + infor.move + Math.abs(infol.cnt - infol.sum) + Math.abs(infor.cnt - infor.sum);
    return new Info(cnts, sums, moves);
  }
}

code5 P1352 没有上司的舞会

// 没有上司的舞会

// 某大学有n个职员,编号为1…n

// 他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树

// 父结点就是子结点的直接上司

// 现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数

// 但是如果某个职员的直接上司来参加舞会了

// 那么这个职员就无论如何也不肯来参加舞会了

// 所以请你编程计算邀请哪些职员可以使快乐指数最大

// 返回最大的快乐指数。

// 测试链接 : https://www.luogu.com.cn/problem/P1352

// 本题和讲解037的题目7类似

// 链式链接 : https://leetcode.cn/problems/house-robber-iii/

// 请同学们务必参考如下代码中关于输入、输出的处理

// 这是输入输出处理效率很高的写法

// 提交以下的code,提交时请把类名改成"Main",可以直接通过

以x为头的数

x来:x+以a为头的来数+以b为头的来数+以c为头的来数+…

x不来:以a为头的数max(来,不来)+以b为头的数max(来,不来)+以c为头的数max(来,不来)

整合返回(来,不来)

package class078;
// 没有上司的舞会
// 某大学有n个职员,编号为1...n
// 他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树
// 父结点就是子结点的直接上司
// 现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 
// 但是如果某个职员的直接上司来参加舞会了
// 那么这个职员就无论如何也不肯来参加舞会了
// 所以请你编程计算邀请哪些职员可以使快乐指数最大
// 返回最大的快乐指数。
// 测试链接 : https://www.luogu.com.cn/problem/P1352
// 本题和讲解037的题目7类似
// 链式链接 : https://leetcode.cn/problems/house-robber-iii/
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
public class Code05_Dancing {
  public static int MAXN = 6001;
  public static int[] nums = new int[MAXN];
  public static boolean[] boss = new boolean[MAXN];
  // 链式前向星建图
  public static int[] head = new int[MAXN];
  public static int[] next = new int[MAXN];
  public static int[] to = new int[MAXN];
  public static int cnt;
  // 动态规划表
  // no[i] : i为头的整棵树,在i不来的情况下,整棵树能得到的最大快乐值
  public static int[] no = new int[MAXN];
  // no[i] : i为头的整棵树,在i来的情况下,整棵树能得到的最大快乐值
  public static int[] yes = new int[MAXN];
  public static int n, h;
  public static void build(int n) {
    Arrays.fill(boss, 1, n + 1, true);
    Arrays.fill(head, 1, n + 1, 0);
    cnt = 1;
  }
  public static void addEdge(int u, int v) {
    next[cnt] = head[u];
    to[cnt] = v;
    head[u] = cnt++;
  }
  public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    StreamTokenizer in = new StreamTokenizer(br);
    PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    while (in.nextToken() != StreamTokenizer.TT_EOF) {
      n = (int) in.nval;
      build(n);
      for (int i = 1; i <= n; i++) {
        in.nextToken();
        nums[i] = (int) in.nval;
      }
      for (int i = 1, low, high; i < n; i++) {
        in.nextToken();
        low = (int) in.nval;
        in.nextToken();
        high = (int) in.nval;
        addEdge(high, low);
        boss[low] = false;
      }
      for (int i = 1; i <= n; i++) {
        if (boss[i]) {
          h = i;
          break;
        }
      }
      f(h);
      out.println(Math.max(no[h], yes[h]));
    }
    out.flush();
    out.close();
    br.close();
  }
  public static void f(int u) {
    no[u] = 0;
    yes[u] = nums[u];
    for (int ei = head[u], v; ei > 0; ei = next[ei]) {
      v = to[ei];
      f(v);
      no[u] += Math.max(no[v], yes[v]);
      yes[u] += no[v];
    }
  }
}

code6 968. 监控二叉树

// 监控二叉树

// 给定一个二叉树,我们在树的节点上安装摄像头

// 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象

// 计算监控树的所有节点所需的最小摄像头数量

// 测试链接 : https://leetcode.cn/problems/binary-tree-cameras/

三个状态

0:x无覆盖,但下方全覆盖了

1:x有覆盖,x上无相机

2:x有覆盖,x上有相机

左右有任何1个0,x放回2

左右都是1,x放回0

左右有12 21 22,x放回0

空树返回1

package class078;
// 监控二叉树
// 给定一个二叉树,我们在树的节点上安装摄像头
// 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象
// 计算监控树的所有节点所需的最小摄像头数量
// 测试链接 : https://leetcode.cn/problems/binary-tree-cameras/
public class Code06_BinaryTreeCameras {
  // 不要提交这个类
  public static class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
  }
  // 提交如下的方法
  public int minCameraCover(TreeNode root) {
    ans = 0;
    if (f(root) == 0) {
      ans++;
    }
    return ans;
  }
  // 遍历过程中一旦需要放置相机,ans++
  public static int ans;
  // 递归含义
  // 假设x上方一定有父亲的情况下,这个假设很重要
  // x为头的整棵树,最终想都覆盖,
  // 并且想使用最少的摄像头,x应该是什么样的状态
  // 返回值含义
  // 0: x是无覆盖的状态,x下方的节点都已经被覆盖
  // 1: x是覆盖状态,x上没摄像头,x下方的节点都已经被覆盖
  // 2: x是覆盖状态,x上有摄像头,x下方的节点都已经被覆盖
  private int f(TreeNode x) {
    if (x == null) {
      return 1;
    }
    int left = f(x.left);
    int right = f(x.right);
    if (left == 0 || right == 0) {
      ans++;
      return 2;
    }
    if (left == 1 && right == 1) {
      return 0;
    }
    return 1;
  }
}

code7 437. 路径总和 III

// 路径总和 III

// 给定一个二叉树的根节点 root ,和一个整数 targetSum

// 求该二叉树里节点值之和等于 targetSum 的 路径 的数目

// 路径 不需要从根节点开始,也不需要在叶子节点结束

// 但是路径方向必须是向下的(只能从父节点到子节点)

// 测试链接 : https://leetcode.cn/problems/path-sum-iii/

sum : 从头节点出发,来到x的时候,上方累加和是多少

路径必须以x作为结尾,路径累加和是target的路径数量,累加到全局变量ans上

package class078;
import java.util.HashMap;
// 路径总和 III
// 给定一个二叉树的根节点 root ,和一个整数 targetSum
// 求该二叉树里节点值之和等于 targetSum 的 路径 的数目
// 路径 不需要从根节点开始,也不需要在叶子节点结束
// 但是路径方向必须是向下的(只能从父节点到子节点)
// 测试链接 : https://leetcode.cn/problems/path-sum-iii/
public class Code07_PathSumIII {
  // 不要提交这个类
  public static class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
  }
  // 提交如下的方法
  public static int pathSum(TreeNode root, int sum) {
    HashMap<Long, Integer> presum = new HashMap<>();
    presum.put(0L, 1);
    ans = 0;
    f(root, sum, 0, presum);
    return ans;
  }
  public static int ans;
  // sum : 从头节点出发,来到x的时候,上方累加和是多少
  // 路径必须以x作为结尾,路径累加和是target的路径数量,累加到全局变量ans上
  public static void f(TreeNode x, int target, long sum, HashMap<Long, Integer> presum) {
    if (x != null) {
      sum += x.val; // 从头节点出发一路走到x的整体累加和
      ans += presum.getOrDefault(sum - target, 0);
      presum.put(sum, presum.getOrDefault(sum, 0) + 1);
      f(x.left, target, sum, presum);
      f(x.right, target, sum, presum);
      presum.put(sum, presum.get(sum) - 1);
    }
  }
}


相关文章
|
2月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
66 1
|
4月前
|
存储 算法 Java
Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。
【6月更文挑战第21天】Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。二叉树遍历通过访问根、左、右子节点实现。DFS采用递归遍历图的节点,而BFS利用队列按层次访问。以下是简化的代码片段:[Java代码略]
39 4
|
4月前
|
存储 算法 Linux
【数据结构和算法】---二叉树(1)--树概念及结构
【数据结构和算法】---二叉树(1)--树概念及结构
40 0
|
6天前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
20 2
|
3月前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
【7月更文挑战第19天】Trie树,又称前缀树,是优化字符串搜索的高效数据结构。通过利用公共前缀,Trie树能快速插入、删除和查找字符串。
83 2
|
3月前
|
机器学习/深度学习 数据采集 算法
Python实现GBDT(梯度提升树)回归模型(GradientBoostingRegressor算法)项目实战
Python实现GBDT(梯度提升树)回归模型(GradientBoostingRegressor算法)项目实战
|
3月前
|
机器学习/深度学习 数据采集 算法
Python实现GBDT(梯度提升树)分类模型(GradientBoostingClassifier算法)并应用网格搜索算法寻找最优参数项目实战
Python实现GBDT(梯度提升树)分类模型(GradientBoostingClassifier算法)并应用网格搜索算法寻找最优参数项目实战
112 3
|
3月前
|
算法 JavaScript
JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)
JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)
56 0
JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)
|
4月前
|
机器学习/深度学习 数据采集 存储
算法金 | 决策树、随机森林、bagging、boosting、Adaboost、GBDT、XGBoost 算法大全
**摘要:** 这篇文章介绍了决策树作为一种机器学习算法,用于分类和回归问题,通过一系列特征测试将复杂决策过程简化。文章详细阐述了决策树的定义、构建方法、剪枝优化技术,以及优缺点。接着,文章讨论了集成学习,包括Bagging、Boosting和随机森林等方法,解释了它们的工作原理、优缺点以及如何通过结合多个模型提高性能和泛化能力。文中特别提到了随机森林和GBDT(XGBoost)作为集成方法的实例,强调了它们在处理复杂数据和防止过拟合方面的优势。最后,文章提供了选择集成学习算法的指南,考虑了数据特性、模型性能、计算资源和过拟合风险等因素。
52 0
算法金 | 决策树、随机森林、bagging、boosting、Adaboost、GBDT、XGBoost 算法大全
|
4月前
|
机器学习/深度学习 算法
梯度提升树GBDT系列算法
在Boosting集成算法当中,我们逐一建立多个弱评估器(基本是决策树),并且下一个弱评估器的建立方式依赖于上一个弱评估器的评估结果,最终综合多个弱评估器的结果进行输出。
下一篇
无影云桌面