Leetcode834. 树中距离之和

news2025/3/12 13:17:47

Every day a Leetcode

题目来源:834. 树中距离之和

解法1:换根 DP

题解:【图解】一张图秒懂换根 DP!(Python/Java/C++/Go/JS)

暴力做法是,以点 i 为为树根,从 i 出发对树进行深度优先搜索,那么 i 到 j 的距离就是 j 在这棵树的深度,所有点的深度之和就是 answer[i]。

但这样做,DFS 一次的时间是 O(n),n 个节点个点各 DFS 一次,总时间就是 O(n2),会超时。如何优化呢?

请添加图片描述

子树大小是怎么算的?

先说二叉树,子树 x 的大小等于左子树的大小,加上右子树的大小,再加上 1(节点 x 本身),那么后序遍历这棵树,就可以算出每棵子树的大小。然后推广到一般树,子树 x 的大小,等于 x 的所有儿子的子树大小之和,再加上 1(节点 x 本身)。

问:这种算法的本质是什么?

答:以图中的这棵树为例,从「以 0 为根」换到「以 2 为根」时,原来 2 的子节点还是 2 的子节点,原来 1 的子节点还是 1 的子节点,唯一改变的是 0 和 2 的父子关系。由此可见,一对节点的距离的「变化量」应该是很小的,那么找出「变化量」的规律,就可以基于 answer[0] 算出 answer[2] 了。这种算法叫做换根 DP。

代码:

/*
 * @lc app=leetcode.cn id=834 lang=cpp
 *
 * [834] 树中距离之和
 */

// @lc code=start

// 换根 DP(树型 DP 的变种)

class Solution
{
public:
    vector<int> sumOfDistancesInTree(int n, vector<vector<int>> &edges)
    {
        // g[x] 表示 x 的所有邻居
        vector<vector<int>> g(n);
        for (auto &edge : edges)
        {
            int x = edge[0], y = edge[1];
            g[x].push_back(y);
            g[y].push_back(x);
        }
        // size[i] 为 子树 i 的大小
        vector<int> size(n, 1); // 注意这里初始化成 1 了,下面只需要累加儿子的子树大小
        vector<int> answer(n);

        function<void(int, int, int)> dfs = [&](int x, int father, int depth)
        {
            answer[0] += depth; // depth 为 0 到 x 的距离
            // 遍历 x 的邻居 y
            for (int &y : g[x])
                if (y != father) // 避免访问父节点
                {
                    dfs(y, x, depth + 1); // x 是 y 的父节点
                    size[x] += size[y];   // 累加 x 的儿子 y 的子树大小
                }
        };

        dfs(0, -1, 0); // 0 没有父节点

        function<void(int, int)> reRoot = [&](int x, int father)
        {
            // 遍历 x 的邻居 y
            for (int y : g[x])
            {
                if (y != father) // 避免访问父节点
                {
                    answer[y] = answer[x] + n - 2 * size[y];
                    reRoot(y, x); // x 是 y 的父节点
                }
            }
        };

        reRoot(0, -1); // 0 没有父节点

        return answer;
    }
};
// @lc code=end

结果:

在这里插入图片描述

复杂度分析:

时间复杂度:O(n),其中 n 是树的节点个数。DFS 两次,每次 DFS 会递归访问每个节点恰好一次,所以时间复杂度为 O(n)。

空间复杂度:O(n),其中 n 是树的节点个数。

拓展题:2858. 可以到达每一个节点的最少边反转次数

题目链接:2858. 可以到达每一个节点的最少边反转次数

代码:

/*
 * @lc app=leetcode.cn id=2858 lang=cpp
 *
 * [2858] 可以到达每一个节点的最少边反转次数
 */

// @lc code=start
class Solution
{
public:
    vector<int> minEdgeReversals(int n, vector<vector<int>> &edges)
    {
        // g[x] 表示 x 的所有邻居
        vector<vector<pair<int, int>>> g(n);
        for (auto &edge : edges)
        {
            int x = edge[0], y = edge[1];
            // x->y 视为正向,标记为 1;反向标记为 -1
            g[x].push_back(pair<int, int>(y, 1));
            g[y].push_back(pair<int, int>(x, -1));
        }
        // size[i] 为 子树 i 的大小
        // vector<int> size(n, 1); // 注意这里初始化成 1 了,下面只需要累加儿子的子树大小
        vector<int> answer(n);

        function<void(int, int)> dfs = [&](int x, int father)
        {
            // 遍历 x 的邻居 y
            for (auto &[y, dir] : g[x])
                if (y != father) // 避免访问父节点
                {
                    answer[0] += dir < 0;
                    dfs(y, x); // x 是 y 的父节点
                }
        };

        dfs(0, -1); // 0 没有父节点

        function<void(int, int)> reRoot = [&](int x, int father)
        {
            // 遍历 x 的邻居 y
            for (auto &[y, dir] : g[x])
                if (y != father) // 避免访问父节点
                {
                    answer[y] = answer[x] + dir;
                    reRoot(y, x); // x 是 y 的父节点
                }
        };

        reRoot(0, -1); // 0 没有父节点

        return answer;
    }
};
// @lc code=end

复杂度分析:

时间复杂度:O(n),其中 n 是树的节点个数。DFS 两次,每次 DFS 会递归访问每个节点恰好一次,所以时间复杂度为 O(n)。

空间复杂度:O(n),其中 n 是树的节点个数。

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

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

相关文章

【MATLAB源码-第135期】基于matlab的变色龙群优化算法CSA)机器人栅格路径规划,输出做短路径图和适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 变色龙群优化算法&#xff08;Chameleon Swarm Algorithm&#xff0c;CSA&#xff09;是一种新颖的群体智能优化算法&#xff0c;受到自然界中变色龙捕食和社交行为的启发。变色龙以其独特的适应能力而著称&#xff0c;能够根…

一分钟了解电脑关机快捷键是什么!

在日常使用电脑的过程中&#xff0c;了解一些基本的快捷键是提高效率的关键之一。其中&#xff0c;电脑关机快捷键是一个方便且迅速的操作&#xff0c;使您可以在不用通过烦琐的菜单操作的情况下&#xff0c;快速关机电脑。在本文中&#xff0c;我们将探讨电脑关机快捷键是什么…

【vue3学习笔记】自定义hook;toRef与toRefs

尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通 课程 P156节 《自定义hook》笔记&#xff1a; 实现一个鼠标“打点”功能&#xff1a; 注意点&#xff1a; &#xff08;1&#xff09;组件卸载时需要接触window上的事件绑定&#xff0c;否则组件卸载后window上绑定的事件还在生…

R语言学习case10:ggplot基础画图Parallel Coordinate Plot 平行坐标图

step1: 导入ggplot2库文件 library(ggplot2)step2&#xff1a;带入自带的iris数据集 iris <- datasets::irisstep3&#xff1a;查看数据信息 dim(iris)维度为 [150,5] head(iris)查看数据前6行的信息 step4&#xff1a;利用ggplot工具包绘图 plot5 <- ggparcoord(…

mac如何实现升级node版本、切换node版本

一、 查看node所有版本&#xff08;前提:安装了nodejs&#xff09; npm view node versions二、安装指定node版本 sudo n 版本号三、检查目前安装了哪些版本的node&#xff0c;会出现已安装的node版本 n四、切换已安装的node版本 sudo n 版本号其他命令 1、sudo npm cache…

ChatGPT之制作短视频

引言 今天带来了如何使用 ChatGPT和剪映来制作简单的短视频教程&#xff0c;在这其中 ChatGPT的作用主要是帮我们生成文案&#xff0c;剪映的功能就是根据文案自动生成视频&#xff0c;并配上一些图片、动画、字幕和解说。 ChatGPT生成文案 首先&#xff0c;我们需要使用提示…

Open CASCADE学习|拉伸

目录 1、沿方向拉伸 2、沿路径拉伸 3、变形拉伸 1、沿方向拉伸 #include <Geom_CylindricalSurface.hxx> #include <gp_Ax3.hxx> #include <GeomAPI_Interpolate.hxx> #include <BRepAdaptor_Curve.hxx> #include <BRepBuilderAPI_MakeEdge.hxx&…

挖矿系列:细说Python、conda 和 pip 之间的关系

继续挖矿&#xff0c;挖金矿&#xff01; 1. Python、conda 和 pip Python、conda 和 pip 是在现代数据科学和软件开发中常用的工具&#xff0c;它们各自有不同的作用&#xff0c;但相互之间存在密切的关系&#xff1a; Python&#xff1a;是一种解释型、面向对象的高级程序设…

Jenkins升级后,构建任务配置界面重复错位

最近我把公司的Jenkins服务升级到了最新版本&#xff0c;升级完成后&#xff0c;点了一下构建任务&#xff0c;发现能够构建成功&#xff0c;就以为顺利完成升级了&#xff0c;下班走了&#xff0c;结果第二天&#xff0c;进入构建任务配置界面发现&#xff0c;界面一团乱麻&am…

Django的web框架Django Rest_Framework精讲(四)

文章目录 1.DRF认证组件Authentication2.权限Permissions3.限流Throttling4.过滤Filtering5.排序6.分页Pagination7.异常处理 Exceptions8.自动生成接口文档 大家好&#xff0c;我是景天&#xff0c;今天我们继续DRF的最后一讲&#xff0c;Django的web框架Django Rest_Framewor…

边缘计算网关在智能制造中有哪些应用?-天拓四方

在智能制造和工业生产环境中&#xff0c;数据已经成为新的生产要素&#xff0c;工业生产对实时性、灵活性和智能化也提出了更高的要求。而在这个过程中&#xff0c;边缘计算网关发挥着不可或缺的作用。它作为设备层与网络层之间的关键桥梁&#xff0c;确保了数据的实时、高效处…

Unity3d Cinemachine篇(六)— TargetGroup

文章目录 前言使用TargetGroup追随多个模型1. 创建二个游戏物体2. 创建TargetGroup相机3. 设置相机4. 完成 前言 上一期我们简单的使用了ClearShot相机&#xff0c;这次我们来使用一下TargetGroup 使用TargetGroup追随多个模型 1. 创建二个游戏物体 2. 创建TargetGroup相机 3…

自学Java的第十九天

一&#xff0c;每日收获 1.排序 2.冒泡排序法 3.查找 4.多维数组-二维数组 二&#xff0c;新名词与小技巧 三&#xff0c;今天学习中所遇到的困难 一&#xff0c;每日收获 1.排序 ① 排序的介绍 排序是将多个数据&#xff0c;依指定的顺序进行排列的过程。 ② 排序的…

kafka客户端生产者消费者kafka可视化工具(可生产和消费消息)

点击下载《kafka客户端生产者消费者kafka可视化工具&#xff08;可生产和消费消息&#xff09;》 1. 前言 因在工作中经常有用到kafka做消息的收发&#xff0c;每次调试过程中&#xff0c;经常需要查看接收的消息内容以及人为发送消息&#xff0c;从网上搜寻了一下&#xff0…

设计一个可以智能训练神经网络的流程

设计一个可以智能训练神经网络的流程,需要考虑以下几个关键步骤: 初始化参数:设定初始的batch size和learning rate,以及其他的神经网络参数。训练循环:开始训练过程,每次迭代更新网络的权重。监控loss:在每个训练周期(epoch)后,监控loss的变化情况。动态调整:根据l…

redis大数据统计之hyperloglog,GEO,Bitmap

目录 一、亿级系统常见的四中统计 1、聚合统计 2、排序统计 3、二值统计 4、基数统计 二、hyperloglog 去重的方式有哪些&#xff1f; hyperloglog实战演示 1、技术选型 2、代码演示 三、GEO GEO实战演示 四、Bitmap 一、亿级系统常见的四中统计 1、聚合统计 聚…

服务器和云计算之间有什么关系?

云计算与服务器之间的关系是密切而复杂的。首先&#xff0c;我们需要明确一点&#xff0c;云计算并不是一种全新的技术&#xff0c;而是对现有技术的一种整合和改进。在这个基础上&#xff0c;我们可以更好地理解云计算与服务器之间的关系。 服务器是云计算的重要组成部分之一…

SpringCloud-生产者和消费者

一、生产者和消费者的定义 在 Spring Cloud 中&#xff0c;术语 "生产者" 和 "消费者" 用于描述微服务架构中的两种基本角色。 角色定义生产者 Provider生产者是提供具体服务或功能的模块。它将业务逻辑封装成服务&#xff0c;供其他模块调用。生产者向服…

线性表 —— 数组、栈、队、链表

本文以 typescript 实现数据结构&#xff0c;虽说是 ts 实现&#xff0c;但更准确说是面向对象的方式实现&#xff0c;因此可以无缝切换成 Java 等面向对象语言。 什么是数据结构&#xff08;Data Structure&#xff09;&#xff1f; “数据结构是ADT&#xff08;抽象数据类型…

一文学会Axios的使用

异步请求 同步发送请求过程如下 浏览器页面在发送请求给服务器&#xff0c;在服务器处理请求的过程中&#xff0c;浏览器页面不能做其他的操作。只能等到服务器响应结束后才能&#xff0c;浏览器页面才能继续做其他的操作。 异步发送请求过程如下浏览器页面发送请求给服务器&…