[LeetCode] 834. Sum of Distances in Tree 树中距离之和
An undirected, connected tree with `N` nodes labelled `0...N-1` and `N-1` `edges` are given.
The ith edge connects nodes edges[i][0] and edges[i][1] together.
Return a list ans, where ans[i] is the sum of the distances between node i and all other nodes.
Example 1:
Input: N = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
Output: [8,12,6,10,10,10]
Explanation:
Here is a diagram of the given tree:
0
/ \
1 2
/|\
3 4 5
We can see that dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5)
equals 1 + 1 + 2 + 2 + 2 = 8. Hence, answer[0] = 8, and so on.
这道题给了一棵树,实际上是无向图,让求每个结点到其他所有结点的距离之和。这里并没有定义树结构,而是给了每条边的两端结点,那么还是先建立邻接链表吧,然后当作无向图来处理吧。由于结点的个数为N,所以直接用二维数组建立邻接链表,注意无向图是双向的。好,现在表示树的数据结构有了,该如何求距离之和呢?先从最简单的例子还是看吧,假如只有一个结点的话,由于不存在其他结点,则也没有距离之说,所以是0。若有连两个结点,比如下面所示:
0
/
1
对于结点0来说,距离之和为1,因为只有结点1距离其为1,此时结点0只有1个子结点。若有三个结点的话,比如:
0
/ \
1 2
则所有结点到结点0的距离之和为2,而结点0也正好有两个子结点,是不是有某种联系呢,还是说我们想多了?再来看一个稍稍复杂些的例子吧:
0
/ \
1 2
/ \
3 4
这里的话所有结点到结点0的距离之和为6,显然不是子结点的个数,整个树也就5个结点。对于左子树,这个正好是上一个讨论的例子,左子树中到结点1的距离之和为2,而左子树总共有3个结点,加起来是5。而右子树只有一个结点2,在右子树中的距离之和为0,右子树总共有1个结点,5加上1,正好是6?恭喜,这就是算每个子树中的结点到子树根结的距离之和的方法,即所有子结点的距离之和加上以子结点为根的子树结点个数。说的好晕啊,用代码来表示吧,需要两个数组 count 和 res,其中 count[i] 表示以结点i为根结点的子树中结点的个数,res[i] 表示其他所有结点到结点i的距离之和。根据上面的规律,可以总结出下面两个式子:
count[root] = sum(count[i]) + 1
res[root] = sum(res[i]) + sum(count[i])
这里的 root 表示所有的子树的根结点,i表示的是 root 的相连子结点,注意必须是相连的,这里不一定是二叉树,所有可能会有多个子结点。另外需要注意的是这里的 res[root] 表示的是以 root 为根结点的子树中所有的结点到 root 的距离之和,其他非子树中结点的距离之和还没有统计。可以发现这两个式子中当前结点的值都是由其子结点决定的,这种由下而上的特点天然适合用后序遍历来做,可以参见这道题 Binary Tree Postorder Traversal,还好这里不用写迭代形式的后序遍历,用递归写就简单的多了。同时还要注意的是用邻接链表表示的无向图遍历时,为了避免死循环,一般是要记录访问过的结点的,这里由于是树的结构,不会存在环,所以可以简单化,直接记录上一个结点 pre 就行了,只有当前结点i和 pre 不同才继续处理。
好,更新完了所有的 count[root] 和 res[root] 之后,就要来更新所有的 res[i] 了,因为上面的讲解提到了 res[root] 表示的是以 root 为根结点的子树中所有的结点到 root 的距离,那么子树之外的结点到 root 的距离也得加上,才是最终要求的 res[i]。虽然现在还没有更新所有的 res[i],但是有一个结点的 res 值是正确的,就是整个树的根结点,这个真正的 res[root] 值是正确的!现在假设要计算 root 结点的一个子结点i的 res 值,即要计算所有结点到结点i的距离,此时知道以结点i为根结点的子树的总结点个数为 count[i],而这 count[i] 个结点之前在算 res[root] 时是到根结点 root 的距离,但是现在只要计算到结点i的距离,所以这 count[i] 个结点的距离都少了1,而其他所有的结点,共 N - count[i] 个,离结点i的距离比离 root 结点的距离都增加了1,所以 res[i] 的更新方法如下:
res[i] = res[root] - count[i] + N - count[i]
这里是从上而下的更新,可以使用最常用的先序遍历,可以参见这道题 Binary Tree Preorder Traversal,这样更新下来,所有的 res[i] 就都是题目中要求的值了,参见代码如下:
class Solution {
public:
vector<int> sumOfDistancesInTree(int N, vector<vector<int>>& edges) {
vector<int> res(N), count(N);
vector<vector<int>> tree(N);
for (auto &edge : edges) {
tree[edge[0]].push_back(edge[1]);
tree[edge[1]].push_back(edge[0]);
}
helper(tree, 0, -1, count, res);
helper2(tree, 0, -1, count, res);
return res;
}
void helper(vector<vector<int>>& tree, int cur, int pre, vector<int>& count, vector<int>& res) {
for (int i : tree[cur]) {
if (i == pre) continue;
helper(tree, i, cur, count, res);
count[cur] += count[i];
res[cur] += res[i] + count[i];
}
++count[cur];
}
void helper2(vector<vector<int>>& tree, int cur, int pre, vector<int>& count, vector<int>& res) {
for (int i : tree[cur]) {
if (i == pre) continue;
res[i] = res[cur] - count[i] + count.size() - count[i];
helper2(tree, i, cur, count, res);
}
}
};
讨论:整体来说,这道题算是相当有难度的一道题,同时考察了邻接链表的建立,无向图的遍历,树的先序和后序遍历,以及对复杂度的拆分能力,总之是非常棒的一道题,博主非常喜欢~
Github 同步地址:
https://github.com/grandyang/leetcode/issues/834
类似题目:
Binary Tree Postorder Traversal
Binary Tree Preorder Traversal
Distribute Coins in Binary Tree
参考资料:
https://leetcode.com/problems/sum-of-distances-in-tree/
https://leetcode.com/problems/sum-of-distances-in-tree/discuss/161975/My-DFS-sulotion-two-passes
[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)
[LeetCode] 834. Sum of Distances in Tree 树中距离之和的更多相关文章
- [LeetCode] 834. Sum of Distances in Tree
LeetCode刷题记录 传送门 Description An undirected, connected treewith N nodes labelled 0...N-1 and N-1 edge ...
- 834. Sum of Distances in Tree —— weekly contest 84
Sum of Distances in Tree An undirected, connected tree with N nodes labelled 0...N-1 and N-1 edges a ...
- 【leetcode】834. Sum of Distances in Tree(图算法)
There is an undirected connected tree with n nodes labeled from 0 to n - 1 and n - 1 edges. You are ...
- Java实现 LeetCode 834 树中距离之和(DFS+分析)
834. 树中距离之和 给定一个无向.连通的树.树中有 N 个标记为 0-N-1 的节点以及 N-1 条边 . 第 i 条边连接节点 edges[i][0] 和 edges[i][1] . 返回一个表 ...
- [Swift]LeetCode834. 树中距离之和 | Sum of Distances in Tree
An undirected, connected tree with N nodes labelled 0...N-1 and N-1 edges are given. The ith edge co ...
- 树中的路径和 Sum of Distances in Tree
2019-03-28 15:25:43 问题描述: 问题求解: 写过的最好的Hard题之一. 初看本题,很经典的路径和嘛,dfs一遍肯定可以得到某个节点到其他所有节点的距离和.这种算法的时间复杂度是O ...
- leetcode834 Sum of Distances in Tree
思路: 树形dp. 实现: class Solution { public: void dfs(int root, int p, vector<vector<int>>& ...
- LeetCode 404. Sum of Left Leaves (左子叶之和)
Find the sum of all left leaves in a given binary tree. Example: 3 / \ 9 20 / \ 15 7 There are two l ...
- [LeetCode] Two Sum III - Data structure design 两数之和之三 - 数据结构设计
Design and implement a TwoSum class. It should support the following operations:add and find. add - ...
随机推荐
- CocoaPods 升级1.8.4的坑 CDN: trunk Repo update failed
之前升级了cocoaPods 版本1.8.4,今天pod install,然后问题就来了: 1.出现了下边的问题: Adding spec repo `trunk` with CDN `https:/ ...
- python-10-列表、元组嵌套
前言 元组.列表前面章节有讲解实例,本节内容是列表.元组的多嵌套. 一.列表嵌套 1.列表嵌套操作1 # 列表的嵌套 li = ['xiaolong', '小林', ['小龙', 'xiaol'], ...
- 【shell脚本】检查内存使用情况===chenkMen.sh
检查内存使用情况,当内存可使用等于100时,释放缓存 [root@localhost thy]# cat checkMem.sh #!/bin/bash #防止内存溢出问题 used=`free -m ...
- 给 VS2017、VS2019 安装 ILSpy 插件
关于 ILSpy is the open-source .NET assembly browser and decompiler. ILSpy 主页地址:https://github.com/icsh ...
- Kubernetes 动态PV使用
Kubernetes 动态PV使用 Kubernetes支持动态供给的存储插件:https://kubernetes.io/docs/concepts/storage/storage-classes/ ...
- CSS 选择器大全
在CSS中,选择器是用于选择要设置样式的元素的模式. 选择器 例子 描述 .class .intro 选择class=“intro”的所有元素 #id #firstname 选择id=“firstna ...
- 原生JavaScript HTML DOM Style 对象参考
Style 对象属性 可以在Style对象上使用以下属性: “CSS”列指示定义属性的CSS版本(CSS1,CSS2或CSS3). 属性 描述 CSS alignContent 当项目不使用所有可用空 ...
- sublime_text运行python ctrl+b运行的界面隐藏了怎么重新调出来恢复显示?
sublime_text运行python ctrl+b运行的界面隐藏了怎么重新调出来恢复显示?搜索了下都是说怎么隐藏的,隐藏后怎么恢复显示的没找到看进程还在运行,但调不出来看运行结果了,console ...
- CI/CD DevOps
CI/CD DevOps 通过技术工具链完成持续集成CI.持续交付CD.用户反馈和系统优化的整合,实现跨团队的无缝协作(DevOps). 什么是持续集成? 他是开发每天代码更新的副本,所有的开发工作都 ...
- SQL的概念与发展 - 极客时间学习笔记
了解SQL SQL的两个重要标准是SQL92和SQL99. SQL语言的划分 DDL,也叫Data Definition Language,也就是数据定义语言,用来定义数据库对象,包括数据库.数据表和 ...