diff --git a/Contents/00.Introduction/04.Solutions-List.md b/Contents/00.Introduction/04.Solutions-List.md index 49e75d6f..db762051 100644 --- a/Contents/00.Introduction/04.Solutions-List.md +++ b/Contents/00.Introduction/04.Solutions-List.md @@ -1,4 +1,4 @@ -# LeetCode 题解(已完成 798 道) +# LeetCode 题解(已完成 799 道) | 题号 | 标题 | 题解 | 标签 | 难度 | | :------ | :------ | :------ | :------ | :------ | @@ -429,6 +429,7 @@ | 0820 | [单词的压缩编码](https://leetcode.cn/problems/short-encoding-of-words/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0820.%20%E5%8D%95%E8%AF%8D%E7%9A%84%E5%8E%8B%E7%BC%A9%E7%BC%96%E7%A0%81.md) | 字典树、数组、哈希表、字符串 | 中等 | | 0830 | [较大分组的位置](https://leetcode.cn/problems/positions-of-large-groups/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0830.%20%E8%BE%83%E5%A4%A7%E5%88%86%E7%BB%84%E7%9A%84%E4%BD%8D%E7%BD%AE.md) | 字符串 | 简单 | | 0832 | [翻转图像](https://leetcode.cn/problems/flipping-an-image/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0832.%20%E7%BF%BB%E8%BD%AC%E5%9B%BE%E5%83%8F.md) | 数组、双指针、矩阵、模拟 | 简单 | +| 0834 | [树中距离之和](https://leetcode.cn/problems/sum-of-distances-in-tree/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0834.%20%E6%A0%91%E4%B8%AD%E8%B7%9D%E7%A6%BB%E4%B9%8B%E5%92%8C.md) | 树、深度优先搜索、图、动态规划 | 困难 | | 0836 | [矩形重叠](https://leetcode.cn/problems/rectangle-overlap/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0836.%20%E7%9F%A9%E5%BD%A2%E9%87%8D%E5%8F%A0.md) | 几何、数学 | 简单 | | 0841 | [钥匙和房间](https://leetcode.cn/problems/keys-and-rooms/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0841.%20%E9%92%A5%E5%8C%99%E5%92%8C%E6%88%BF%E9%97%B4.md) | 深度优先搜索、广度优先搜索、图 | 中等 | | 0844 | [比较含退格的字符串](https://leetcode.cn/problems/backspace-string-compare/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0844.%20%E6%AF%94%E8%BE%83%E5%90%AB%E9%80%80%E6%A0%BC%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2.md) | 栈、双指针、字符串、模拟 | 简单 | diff --git a/Contents/00.Introduction/05.Categories-List.md b/Contents/00.Introduction/05.Categories-List.md index 9ae97928..b29e06d8 100644 --- a/Contents/00.Introduction/05.Categories-List.md +++ b/Contents/00.Introduction/05.Categories-List.md @@ -1022,7 +1022,7 @@ | 题号 | 标题 | 题解 | 标签 | 难度 | | :------ | :------ | :------ | :------ | :------ | | 0310 | [最小高度树](https://leetcode.cn/problems/minimum-height-trees/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0310.%20%E6%9C%80%E5%B0%8F%E9%AB%98%E5%BA%A6%E6%A0%91.md) | 深度优先搜索、广度优先搜索、图、拓扑排序 | 中等 | -| 0834 | [树中距离之和](https://leetcode.cn/problems/sum-of-distances-in-tree/) | | 树、深度优先搜索、图、动态规划 | 困难 | +| 0834 | [树中距离之和](https://leetcode.cn/problems/sum-of-distances-in-tree/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0834.%20%E6%A0%91%E4%B8%AD%E8%B7%9D%E7%A6%BB%E4%B9%8B%E5%92%8C.md) | 树、深度优先搜索、图、动态规划 | 困难 | | 2581 | [统计可能的树根数目](https://leetcode.cn/problems/count-number-of-possible-root-nodes/) | | 树、深度优先搜索、哈希表、动态规划 | 困难 | ### 状态压缩 DP 题目 diff --git a/Contents/10.Dynamic-Programming/06.Tree-DP/02.Tree-DP-List.md b/Contents/10.Dynamic-Programming/06.Tree-DP/02.Tree-DP-List.md index 65ea97ee..41bd0006 100644 --- a/Contents/10.Dynamic-Programming/06.Tree-DP/02.Tree-DP-List.md +++ b/Contents/10.Dynamic-Programming/06.Tree-DP/02.Tree-DP-List.md @@ -25,6 +25,6 @@ | 题号 | 标题 | 题解 | 标签 | 难度 | | :------ | :------ | :------ | :------ | :------ | | 0310 | [最小高度树](https://leetcode.cn/problems/minimum-height-trees/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0310.%20%E6%9C%80%E5%B0%8F%E9%AB%98%E5%BA%A6%E6%A0%91.md) | 深度优先搜索、广度优先搜索、图、拓扑排序 | 中等 | -| 0834 | [树中距离之和](https://leetcode.cn/problems/sum-of-distances-in-tree/) | | 树、深度优先搜索、图、动态规划 | 困难 | +| 0834 | [树中距离之和](https://leetcode.cn/problems/sum-of-distances-in-tree/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0834.%20%E6%A0%91%E4%B8%AD%E8%B7%9D%E7%A6%BB%E4%B9%8B%E5%92%8C.md) | 树、深度优先搜索、图、动态规划 | 困难 | | 2581 | [统计可能的树根数目](https://leetcode.cn/problems/count-number-of-possible-root-nodes/) | | 树、深度优先搜索、哈希表、动态规划 | 困难 | diff --git a/README.md b/README.md index 41603347..8e615c29 100644 --- a/README.md +++ b/README.md @@ -259,4 +259,4 @@ - [动态规划优化题目](./Contents/10.Dynamic-Programming/11.DP-Optimization/04.DP-Optimization-List.md) ## 11. 附加内容 -## [12. LeetCode 题解(已完成 798 道)](./Contents/00.Introduction/04.Solutions-List.md) \ No newline at end of file +## [12. LeetCode 题解(已完成 799 道)](./Contents/00.Introduction/04.Solutions-List.md) \ No newline at end of file diff --git "a/Solutions/0834. 346円240円221円344円270円255円350円267円235円347円246円273円344円271円213円345円222円214円.md" "b/Solutions/0834. 346円240円221円344円270円255円350円267円235円347円246円273円344円271円213円345円222円214円.md" new file mode 100644 index 00000000..ecc3ac85 --- /dev/null +++ "b/Solutions/0834. 346円240円221円344円270円255円350円267円235円347円246円273円344円271円213円345円222円214円.md" @@ -0,0 +1,108 @@ +# [0834. 树中距离之和](https://leetcode.cn/problems/sum-of-distances-in-tree/) + +- 标签:树、深度优先搜索、图、动态规划 +- 难度:困难 + +## 题目大意 + +**描述**:给定一个无向、连通的树。树中有 $n$ 个标记为 0ドル \sim n - 1$ 的节点以及 $n - 1$ 条边 。 + +给定整数 $n$ 和数组 $edges,ドル其中 $edges[i] = [ai, bi]$ 表示树中的节点 $ai$ 和 $bi$ 之间有一条边。 + +**要求**:返回长度为 $n$ 的数组 $answer,ドル其中 $answer[i]$ 是树中第 $i$ 个节点与所有其他节点之间的距离之和。 + +**说明**: + +- 1ドル \le n \le 3 \times 10^4$。 +- $edges.length == n - 1$。 +- $edges[i].length == 2$。 +- 0ドル \le ai, bi < n$。 +- $ai \ne bi$。 +- 给定的输入保证为有效的树。 + +**示例**: + +- 示例 1: + + + +```Python +输入: n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]] +输出: [8,12,6,10,10,10] +解释: 树如图所示。 +我们可以计算出 dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5) +也就是 1 + 1 + 2 + 2 + 2 = 8。 因此,answer[0] = 8,以此类推。 +``` + +- 示例 2: + + + +```Python +输入: n = 2, edges = [[1,0]] +输出: [1,1] +``` + +## 解题思路 + +### 思路 1:树形 DP + 二次遍历换根法 + +最容易想到的做法是:枚举 $n$ 个节点,以每个节点为根节点进行树形 DP。 + +对于节点 $u,ドル定义 $dp[u]$ 为:以节点 $u$ 为根节点的树,它的所有子节点到它的距离之和。 + +然后进行一轮深度优先搜索,在搜索的过程中得到以节点 $v$ 为根节点的树,节点 $v$ 与所有其他子节点之间的距离之和 $dp[v]$。还能得到子树的节点个数 $sizes[v]$。 + +对于节点 $v$ 来说,其对 $dp[u]$ 的贡献为:节点 $v$ 与所有其他子节点之间的距离之和,再加上需要经过 $u \rightarrow v$ 这条边的节点个数,即 $dp[v] + sizes[v]$。 + +可得到状态转移方程为:$dp[u] = \sum_{v \in graph[u]}(dp[v] + sizes[v])$。 + +这样,对于 $n$ 个节点来说,需要进行 $n$ 次树形 DP,这种做法的时间复杂度为 $O(n^2),ドル而 $n$ 的范围为 $[1, 3 \times 10^4],ドル这样做会导致超时,因此需要进行优化。 + +我们可以使用「二次遍历换根法」进行优化,从而在 $O(n)$ 的时间复杂度内解决这道题。 + +以编号为 0ドル$ 的节点为根节点,进行两次深度优先搜索。 + +1. 第一次遍历:从编号为 0ドル$ 的根节点开始,自底向上地计算出节点 0ドル$ 到其他的距离之和,记录在 $ans[0]$ 中。并且统计出以子节点为根节点的子树节点个数 $sizes[v]$。 +2. 第二次遍历:从编号为 0ドル$ 的根节点开始,自顶向下地枚举每个点,计算出将每个点作为新的根节点时,其他节点到根节点的距离之和。如果当前节点为 $v,ドル其父节点为 $u,ドル则自顶向下计算出 $ans[u]$ 之后,我们将根节点从 $u$ 换为节点 $v,ドル子树上的点到新根节点的距离比原来都小了 1ドル,ドル非子树上剩下所有点到新根节点的距离比原来都大了 1ドル$。则可以据此计算出节点 $v$ 与其他节点的距离和为:$ans[v] = ans[u] + n - 2 \times sizes[u]$。 + +### 思路 1:代码 + +```Python +class Solution: + def sumOfDistancesInTree(self, n: int, edges: List[List[int]]) -> List[int]: + graph = [[] for _ in range(n)] + + for u, v in edges: + graph[u].append(v) + graph[v].append(u) + + + ans = [0 for _ in range(n)] + + sizes = [1 for _ in range(n)] + def dfs(u, fa, depth): + ans[0] += depth + for v in graph[u]: + if v == fa: + continue + dfs(v, u, depth + 1) + sizes[u] += sizes[v] + + def reroot(u, fa): + for v in graph[u]: + if v == fa: + continue + ans[v] = ans[u] + n - 2 * size[v] + reroot(v, u) + + dfs(0, -1, 0) + reroot(0, -1) + return ans +``` + +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(n),ドル其中 $n$ 为树的节点个数。 +- **空间复杂度**:$O(n)$。 +