当前位置: 首页 > news >正文

力扣每日一题——连接两棵树后最大目标节点数目 ||

目录

题目链接:3373. 连接两棵树后最大目标节点数目 II - 力扣(LeetCode)

题目描述

解法一:​​双树贡献分离法​​

Java写法:

C++写法:

运行时间

时间复杂度和空间复杂度

总结


题目链接:3373. 连接两棵树后最大目标节点数目 II - 力扣(LeetCode)

注:下述题目描述和示例均来自力扣

题目描述

        有两棵 无向 树,分别有 n 和 m 个树节点。两棵树中的节点编号分别为[0, n - 1] 和 [0, m - 1] 中的整数。

        给你两个二维整数 edges1 和 edges2 ,长度分别为 n - 1 和 m - 1 ,其中 edges1[i] = [ai, bi] 表示第一棵树中节点 ai 和 bi 之间有一条边,edges2[i] = [ui, vi] 表示第二棵树中节点 ui 和 vi 之间有一条边。

        如果节点 u 和节点 v 之间路径的边数是偶数,那么我们称节点 u 是节点 v 的 目标节点 。注意 ,一个节点一定是它自己的 目标节点 。

        请你返回一个长度为 n 的整数数组 answer ,answer[i] 表示将第一棵树中的一个节点与第二棵树中的一个节点连接一条边后,第一棵树中节点 i 的 目标节点 数目的 最大值 。

注意 ,每个查询相互独立。意味着进行下一次查询之前,你需要先把刚添加的边给删掉。

示例 1:

输入:edges1 = [[0,1],[0,2],[2,3],[2,4]], edges2 = [[0,1],[0,2],[0,3],[2,7],[1,4],[4,5],[4,6]]

输出:[8,7,7,8,8]

解释:

  • 对于 i = 0 ,连接第一棵树中的节点 0 和第二棵树中的节点 0 。
  • 对于 i = 1 ,连接第一棵树中的节点 1 和第二棵树中的节点 4 。
  • 对于 i = 2 ,连接第一棵树中的节点 2 和第二棵树中的节点 7 。
  • 对于 i = 3 ,连接第一棵树中的节点 3 和第二棵树中的节点 0 。
  • 对于 i = 4 ,连接第一棵树中的节点 4 和第二棵树中的节点 4 。

示例 2:

输入:edges1 = [[0,1],[0,2],[0,3],[0,4]], edges2 = [[0,1],[1,2],[2,3]]

输出:[3,6,6,6,6]

解释:

对于每个 i ,连接第一棵树中的节点 i 和第二棵树中的任意一个节点。

提示:

  • 2 <= n, m <= 10^5
  • edges1.length == n - 1
  • edges2.length == m - 1
  • edges1[i].length == edges2[i].length == 2
  • edges1[i] = [ai, bi]
  • 0 <= ai, bi < n
  • edges2[i] = [ui, vi]
  • 0 <= ui, vi < m
  • 输入保证 edges1 和 edges2 都表示合法的树。

解法一:​​双树贡献分离法​

        这道题的解法可以称为​​双树贡献分离法​​。咱们分两部分来说,解核心思路和具体实现。

        首先,问题的核心在于两棵树之间只能连一条边的情况下,如何最大化第一棵树每个节点的目标节点数。这里的关键在于发现:连接后的贡献可以拆分为原本树内的贡献和通过新边获得的第二棵树贡献。举个例子,假设我们在第一棵树的节点A和第二棵树的节点B之间连边,那么A的目标节点数就等于A在原本树里的可达节点数加上B在另一棵树里能带来的额外节点数。

        这里整个过程分两步走:第一步要算出第一棵树每个节点自身在k步内能到达多少个目标节点,这一步可以用DFS遍历整棵树,记录每个节点在限定步数内能找到的节点数。第二步要找出第二棵树中哪个节点能在k-1步内覆盖最多的节点(因为跨树连接需要消耗一步边权),这时候同样用DFS遍历第二棵树的所有节点,找到最大值。

        这里有个比较巧妙的地方:当k=0时根本不能跨树连接,所以这时候第二棵树的贡献直接为零;当k≥1时,跨树后的剩余步数是k-1,这时候只要找到第二棵树中能覆盖最多节点的那个节点即可。整个过程不需要真正连接所有可能的节点对,而是通过预处理两棵树各自的贡献,最后直接相加得到结果。

        实现时要注意两点:1. 使用DFS时要避免重复访问父节点,通过记录父节点指针来防止回头路;2. 在计算第二棵树的贡献时,只需要保留最大值而不需要记录每个节点的贡献,这样能节省内存空间。整个过程的时间复杂度主要取决于两棵树的节点数和k值的大小,属于典型的树遍历问题优化方案。

        这种解法把看似复杂的连接问题拆解成了两个独立的子问题,最后通过简单相加得到结果,既避免了暴力枚举所有可能的连接方式,又保证了算法效率,算是个典型的分治思想在树结构中的应用。

Java写法:

import java.util.*;public class Solution {public int[] maxTargetNodes(int[][] edges1, int[][] edges2) {List<List<Integer>> tree1 = buildTree(edges1);List<List<Integer>> tree2 = buildTree(edges2);// 计算两棵树的层级奇偶分布int[] layerCount1 = computeLayerCounts(tree1);int[] layerCount2 = computeLayerCounts(tree2);// 第二棵树的最大贡献int maxSecond = Math.max(layerCount2[0], layerCount2[1]);// 获取第一棵树每个节点的层级int[] nodeLayers = getNodeLayers(tree1);// 计算结果int[] ans = new int[tree1.size()];for (int i = 0; i < ans.length; i++) {int currentLayer = nodeLayers[i] % 2;ans[i] = (currentLayer == 0 ? layerCount1[0] : layerCount1[1]) + maxSecond;}return ans;}// 构建邻接表private List<List<Integer>> buildTree(int[][] edges) {int n = edges.length + 1;List<List<Integer>> tree = new ArrayList<>();for (int i = 0; i < n; i++) tree.add(new ArrayList<>());for (int[] e : edges) {tree.get(e[0]).add(e[1]);tree.get(e[1]).add(e[0]);}return tree;}// 计算奇偶层节点数(BFS实现)private int[] computeLayerCounts(List<List<Integer>> tree) {int[] layers = new int[tree.size()];Arrays.fill(layers, -1);Queue<Integer> queue = new LinkedList<>();queue.add(0);layers[0] = 0;while (!queue.isEmpty()) {int u = queue.poll();for (int v : tree.get(u)) {if (layers[v] == -1) {layers[v] = layers[u] + 1;queue.add(v);}}}int even = 0, odd = 0;for (int layer : layers) {if (layer % 2 == 0) even++;else odd++;}return new int[]{even, odd};}// 获取每个节点的层级(BFS实现)private int[] getNodeLayers(List<List<Integer>> tree) {int[] layers = new int[tree.size()];Arrays.fill(layers, -1);Queue<Integer> queue = new LinkedList<>();queue.add(0);layers[0] = 0;while (!queue.isEmpty()) {int u = queue.poll();for (int v : tree.get(u)) {if (layers[v] == -1) {layers[v] = layers[u] + 1;queue.add(v);}}}return layers;}
}

C++写法:

#include <vector>
#include <queue>
using namespace std;class Solution {
public:vector<int> maxTargetNodes(vector<vector<int>>& edges1, vector<vector<int>>& edges2) {auto tree1 = buildTree(edges1);auto tree2 = buildTree(edges2);auto [even1, odd1] = computeLayerCounts(tree1);auto [even2, odd2] = computeLayerCounts(tree2);int maxSecond = max(even2, odd2);auto nodeLayers = getNodeLayers(tree1);vector<int> ans(tree1.size());for (int i = 0; i < ans.size(); ++i) {ans[i] = (nodeLayers[i] % 2 ? odd1 : even1) + maxSecond;}return ans;}private:vector<vector<int>> buildTree(vector<vector<int>>& edges) {int n = edges.size() + 1;vector<vector<int>> tree(n);for (auto& e : edges) {tree[e[0]].push_back(e[1]);tree[e[1]].push_back(e[0]);}return tree;}pair<int, int> computeLayerCounts(vector<vector<int>>& tree) {vector<int> layers(tree.size(), -1);queue<int> q;q.push(0);layers[0] = 0;while (!q.empty()) {int u = q.front();q.pop();for (int v : tree[u]) {if (layers[v] == -1) {layers[v] = layers[u] + 1;q.push(v);}}}int even = 0, odd = 0;for (int l : layers) {(l % 2 == 0) ? even++ : odd++;}return {even, odd};}vector<int> getNodeLayers(vector<vector<int>>& tree) {vector<int> layers(tree.size(), -1);queue<int> q;q.push(0);layers[0] = 0;while (!q.empty()) {int u = q.front();q.pop();for (int v : tree[u]) {if (layers[v] == -1) {layers[v] = layers[u] + 1;q.push(v);}}}return layers;}
};

运行时间

时间复杂度和空间复杂度

  • ​时间复杂度​​:O(nk + mk)(DFS)或 O(n² + m²)(BFS)。
  • ​空间复杂度​​:O(n + m + k)



总结

        阿巴阿巴

相关文章:

  • python 包管理工具uv
  • Python基础 | jupyter工具的安装与基本使用
  • 使用k8s服务进行端口代理
  • Parasoft C++Test软件单元测试_常见问题及处理
  • 长安链合约操作 查询合约命令解析
  • 2025年OE SCI2区TOP,进化麻雀搜索算法ESSA+海洋阻尼器迟滞建模与辨识,深度解析+性能实测
  • 本地Markdown开源知识库选型指南
  • 【数据分析】Pandas
  • 4 串电池保护芯片创芯微CM1341-DAT使用介绍
  • 前端面试准备2
  • 工具识别系统Python+深度学习+人工智能+卷积神经网络算法+TensorFlow+图像识别
  • 树莓派超全系列教程文档--(51)如何使用SSH登录树莓派
  • GCN图神经网络的光伏功率预测
  • Linux安装mysql5.7详细教程
  • 02.K8S核心概念
  • DataAgent产品经理(数据智能方向)
  • 系统架构设计师案例分析----经典架构风格特点
  • 若依微服务的定制化服务
  • MATLAB 横向剪切干涉系统用户界面设计及其波前重构研究
  • 安卓添加设备节点权限和selinux访问权限
  • 国人在线做网站/大地资源网在线观看免费
  • 北京网站平台建设公司/网店代运营骗局流程
  • 外贸b2c电子商务网站/友情链接的检查方法
  • 枣庄市政府采购网/多合一seo插件破解版
  • 贵阳微信网站制作/网络营销的六大功能
  • 十大景观设计公司/站长之家seo综合