From 5bd5bdf66e1db7c2c54c5f86919f97feaca64d8f Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Mon, 23 Mar 2026 17:45:51 -0700 Subject: [PATCH 01/13] LeetCode 310: Add step 1 TLE solution --- leetcode/310/memo.md | 27 +++++++++++++++++++++++++++ leetcode/310/step1_tle.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 leetcode/310/memo.md create mode 100644 leetcode/310/step1_tle.py diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md new file mode 100644 index 0000000..0aa762d --- /dev/null +++ b/leetcode/310/memo.md @@ -0,0 +1,27 @@ +# Step 1 + +素直に全ての root を試す方法しか思いつかなかった -> `step1_tle.py`。 + +制約が + +> `1 <= n <= 2 * 10^4` + +で、全ての n から探索を行うと `(2 * 10^4) * (2 * 10^4) = 4 * 10^8`。 +C++ が 1 秒間に `10^8 ~ 10^9` ステップの処理を行えるとすると、Pythonがそれより大体 100 倍遅いから 1 秒間で `10^6 ~ 10^7` ステップ。 +なので、大体 `10 ~ 100` 秒くらいの実行時間になりそう。これは LeetCode 上でギリギリ Time Limit Exceeded になるかならないかくらいなので、微妙。案の定、TLEになった。 + +そのまま実装するとTLEになりそうなのはわかっていたので、何かしらのヒューリスティックが思いつかないか、考えてみて、一つ思い浮かんだのは、枝が一番多いノードを root として選ぶと木の高さが最小になるのでは、と思ったが、幾つか例を試してみると、うまくいかないことがわかった。 + +``` + 5 + | +0 - 1 - 2 - 3 - 4 + | + 6 +``` + +上の例だと、2 が本当の最小の高さを作る root。 + +TODO: Consider the hint + +> How many MHTs can a graph have at most? diff --git a/leetcode/310/step1_tle.py b/leetcode/310/step1_tle.py new file mode 100644 index 0000000..b786bad --- /dev/null +++ b/leetcode/310/step1_tle.py @@ -0,0 +1,29 @@ +class Solution: + def findMinHeightTrees(self, n: int, edges: list[list[int]]) -> list[int]: + node_to_adjacents = collections.defaultdict(list) + for node1, node2 in edges: + node_to_adjacents[node1].append(node2) + node_to_adjacents[node2].append(node1) + + def traverse(node: int, visited: list[bool]) -> int: + if visited[node]: + return 0 + + visited[node] = True + child_height = 0 + for adj in node_to_adjacents[node]: + child_height = max(child_height, traverse(adj, visited)) + visited[node] = False + return 1 + child_height + + visited = [False] * n + root_to_height = {} + for root in range(n): + root_to_height[root] = traverse(root, visited) + + min_height = min(root_to_height.values()) + mht_roots: list[int] = [] + for root, height in root_to_height.items(): + if height == min_height: + mht_roots.append(root) + return mht_roots From fa333eaddafc01ff9182e6d10d656b5198b62525 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Mon, 23 Mar 2026 17:50:03 -0700 Subject: [PATCH 02/13] LeetCode 310: Note possible improvements to step 1 TLE solution --- leetcode/310/memo.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md index 0aa762d..d6b6343 100644 --- a/leetcode/310/memo.md +++ b/leetcode/310/memo.md @@ -2,6 +2,16 @@ 素直に全ての root を試す方法しか思いつかなかった -> `step1_tle.py`。 +後から思ったが、cycle がないことが保証されているので、一つ前に戻るのだけスキップする形の方がシンプルだった。 + +```py + for adj in node_to_adjacents[node]: + if adj == parent: + continue + child_height = max(child_height, 1 + height(adj, node)) + return child_height +``` + 制約が > `1 <= n <= 2 * 10^4` From d6057ad0d5b725b68c77dce96f7c17926d3ea991 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Thu, 26 Mar 2026 12:28:28 -0700 Subject: [PATCH 03/13] LeetCode 310: Note my consideration on the hint given --- leetcode/310/memo.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md index d6b6343..7c7cf7b 100644 --- a/leetcode/310/memo.md +++ b/leetcode/310/memo.md @@ -32,6 +32,9 @@ C++ が 1 秒間に `10^8 ~ 10^9` ステップの処理を行えるとすると 上の例だと、2 が本当の最小の高さを作る root。 -TODO: Consider the hint +LeetCode 上の Hint を考えてみる。 > How many MHTs can a graph have at most? + +幾つか例を考えてみると、雑に考えてグラフの端から端までいく最長のパスかあって、それの真ん中のノードが 1 つか 2 つあり、それが Minimum Height Tree を形成しそう? +結構時間を使ってしまったので、悔しいが、解法を見てしまうことにする。 From 2fa10737f0d5114786b739a9d60bb388ef60675d Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Fri, 27 Mar 2026 20:22:13 -0700 Subject: [PATCH 04/13] LeetCode 310: Note LeetCode solutions/posts --- leetcode/310/memo.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md index 7c7cf7b..8fbed36 100644 --- a/leetcode/310/memo.md +++ b/leetcode/310/memo.md @@ -38,3 +38,10 @@ LeetCode 上の Hint を考えてみる。 幾つか例を考えてみると、雑に考えてグラフの端から端までいく最長のパスかあって、それの真ん中のノードが 1 つか 2 つあり、それが Minimum Height Tree を形成しそう? 結構時間を使ってしまったので、悔しいが、解法を見てしまうことにする。 + +Discord 上で取り組まれた方はいなさそうなので、LeetCode の Solutions を眺めてみると、これらの記事が理解しやすそう + +[dietpepsi - Share some thoughts](https://leetcode.com/problems/minimum-height-trees/solutions/76055/share-some-thoughts-by-dietpepsi-mjsc/) +[lc_1000xCoder - [ Full Explanation ] BFS - Remove Leaf Nodes](https://leetcode.com/problems/minimum-height-trees/solutions/5060930/full-explanation-bfs-remove-leaf-nodes-b-4x00/) + +まず、Minimum Height Tree を作る root は 1 つか 2 つという私の考えは正しかった。それをどう求めるのかだが、各ステップで現在の葉ノードを全て削除していって、最後に残った 1 個か 2 個のノードがグラフの一番長いパスの真ん中 -> Minimum Height Tree の root(s) になる。 From 5918baa7a11fe26cf0d4a0cbfb9628cf51ad554a Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Sat, 28 Mar 2026 12:50:15 -0700 Subject: [PATCH 05/13] LeetCode 310: Add step 1 solution that passes test cases --- leetcode/310/memo.md | 4 +++- leetcode/310/step1.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 leetcode/310/step1.py diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md index 8fbed36..5f3b94f 100644 --- a/leetcode/310/memo.md +++ b/leetcode/310/memo.md @@ -42,6 +42,8 @@ LeetCode 上の Hint を考えてみる。 Discord 上で取り組まれた方はいなさそうなので、LeetCode の Solutions を眺めてみると、これらの記事が理解しやすそう [dietpepsi - Share some thoughts](https://leetcode.com/problems/minimum-height-trees/solutions/76055/share-some-thoughts-by-dietpepsi-mjsc/) -[lc_1000xCoder - [ Full Explanation ] BFS - Remove Leaf Nodes](https://leetcode.com/problems/minimum-height-trees/solutions/5060930/full-explanation-bfs-remove-leaf-nodes-b-4x00/) +[lc\_1000xCoder - [ Full Explanation ] BFS - Remove Leaf Nodes](https://leetcode.com/problems/minimum-height-trees/solutions/5060930/full-explanation-bfs-remove-leaf-nodes-b-4x00/) まず、Minimum Height Tree を作る root は 1 つか 2 つという私の考えは正しかった。それをどう求めるのかだが、各ステップで現在の葉ノードを全て削除していって、最後に残った 1 個か 2 個のノードがグラフの一番長いパスの真ん中 -> Minimum Height Tree の root(s) になる。 + +やり方だけ頭において、LeetCode 207\. Course Schedule のように書いたら、`step1.py` のようになった。 diff --git a/leetcode/310/step1.py b/leetcode/310/step1.py new file mode 100644 index 0000000..1b983d3 --- /dev/null +++ b/leetcode/310/step1.py @@ -0,0 +1,23 @@ +class Solution: + def findMinHeightTrees(self, n: int, edges: list[list[int]]) -> list[int]: + adjacents = [set() for _ in range(n)] + degrees = [0] * n + for node1, node2 in edges: + adjacents[node1].add(node2) + adjacents[node2].add(node1) + degrees[node1] += 1 + degrees[node2] += 1 + + removed = set() + while n - len(removed) > 2: + leaves = [node for node in range(n) if degrees[node] == 1] + for leaf in leaves: + for adj in adjacents[leaf]: + degrees[adj] -= 1 + adjacents[adj].remove(leaf) + + degrees[leaf] -= 1 + adjacents[leaf].pop() + removed.add(leaf) + + return [node for node in range(n) if node not in removed] From c07436269d5351a50a4db7929df6ad7c48b9b892 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Sat, 28 Mar 2026 13:18:28 -0700 Subject: [PATCH 06/13] LeetCode 310: Add step 1 solution that removed linear search --- leetcode/310/memo.md | 4 +++- leetcode/310/step1_improved.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 leetcode/310/step1_improved.py diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md index 5f3b94f..3fff0c3 100644 --- a/leetcode/310/memo.md +++ b/leetcode/310/memo.md @@ -46,4 +46,6 @@ Discord 上で取り組まれた方はいなさそうなので、LeetCode の So まず、Minimum Height Tree を作る root は 1 つか 2 つという私の考えは正しかった。それをどう求めるのかだが、各ステップで現在の葉ノードを全て削除していって、最後に残った 1 個か 2 個のノードがグラフの一番長いパスの真ん中 -> Minimum Height Tree の root(s) になる。 -やり方だけ頭において、LeetCode 207\. Course Schedule のように書いたら、`step1.py` のようになった。 +やり方だけ頭において、LeetCode 207\. Course Schedule のように書いたら、`step1.py` のようになった。しかし、書いていてすぐ、一々各ステップで線形探索して葉ノードを探す (`O(n)`)のではなくて、隣接するノードを更新してそれが葉になり次第 `next_leaves: list[int]` か何かに入れておけば、線形探索をしなくて済むなと思った -> `step1_improved.py`。 + +TODO: Time/Space Complexity diff --git a/leetcode/310/step1_improved.py b/leetcode/310/step1_improved.py new file mode 100644 index 0000000..c61c305 --- /dev/null +++ b/leetcode/310/step1_improved.py @@ -0,0 +1,31 @@ +class Solution: + def findMinHeightTrees(self, n: int, edges: list[list[int]]) -> list[int]: + if n == 1: + return [0] + + adjacents = [set() for _ in range(n)] + degrees = [0] * n + for node1, node2 in edges: + adjacents[node1].add(node2) + adjacents[node2].add(node1) + degrees[node1] += 1 + degrees[node2] += 1 + + leaves = [node for node in range(n) if degrees[node] == 1] + remaining = n + while remaining > 2: + next_leaves = [] + for leaf in leaves: + for adj in adjacents[leaf]: + adjacents[adj].remove(leaf) + degrees[adj] -= 1 + if degrees[adj] == 1: + next_leaves.append(adj) + + degrees[leaf] -= 1 + adjacents[leaf].pop() + remaining -= 1 + + leaves = next_leaves + + return leaves From 25e4fd36bd967980f2cebf42ef69e30213952ce8 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Sat, 28 Mar 2026 16:33:40 -0700 Subject: [PATCH 07/13] LeetCode 310: Tweak step 1 solution --- leetcode/310/memo.md | 2 +- leetcode/310/step1.py | 37 ++++++++++++++++++++++------------ leetcode/310/step1_improved.py | 31 ---------------------------- 3 files changed, 25 insertions(+), 45 deletions(-) delete mode 100644 leetcode/310/step1_improved.py diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md index 3fff0c3..a20afa5 100644 --- a/leetcode/310/memo.md +++ b/leetcode/310/memo.md @@ -46,6 +46,6 @@ Discord 上で取り組まれた方はいなさそうなので、LeetCode の So まず、Minimum Height Tree を作る root は 1 つか 2 つという私の考えは正しかった。それをどう求めるのかだが、各ステップで現在の葉ノードを全て削除していって、最後に残った 1 個か 2 個のノードがグラフの一番長いパスの真ん中 -> Minimum Height Tree の root(s) になる。 -やり方だけ頭において、LeetCode 207\. Course Schedule のように書いたら、`step1.py` のようになった。しかし、書いていてすぐ、一々各ステップで線形探索して葉ノードを探す (`O(n)`)のではなくて、隣接するノードを更新してそれが葉になり次第 `next_leaves: list[int]` か何かに入れておけば、線形探索をしなくて済むなと思った -> `step1_improved.py`。 +やり方だけ頭において、LeetCode 207\. Course Schedule を思い出しながら書いた `step1.py`。 TODO: Time/Space Complexity diff --git a/leetcode/310/step1.py b/leetcode/310/step1.py index 1b983d3..e1e8d8f 100644 --- a/leetcode/310/step1.py +++ b/leetcode/310/step1.py @@ -1,23 +1,34 @@ class Solution: def findMinHeightTrees(self, n: int, edges: list[list[int]]) -> list[int]: - adjacents = [set() for _ in range(n)] + if n == 1: + return [0] + + neighbors = [[] for _ in range(n)] degrees = [0] * n + for node1, node2 in edges: - adjacents[node1].add(node2) - adjacents[node2].add(node1) + neighbors[node1].append(node2) + neighbors[node2].append(node1) degrees[node1] += 1 degrees[node2] += 1 - removed = set() - while n - len(removed) > 2: - leaves = [node for node in range(n) if degrees[node] == 1] + leaves = [node for node in range(n) if degrees[node] == 1] + remaining = n + while remaining > 2: + next_leaves = [] for leaf in leaves: - for adj in adjacents[leaf]: - degrees[adj] -= 1 - adjacents[adj].remove(leaf) + degrees[leaf] = 0 + remaining -= 1 + + # Only one neighbor is active at this point since `leaf` is a leaf + for neighbor in neighbors[leaf]: + if degrees[neighbor] == 0: + continue + + degrees[neighbor] -= 1 + if degrees[neighbor] == 1: + next_leaves.append(neighbor) - degrees[leaf] -= 1 - adjacents[leaf].pop() - removed.add(leaf) + leaves = next_leaves - return [node for node in range(n) if node not in removed] + return leaves diff --git a/leetcode/310/step1_improved.py b/leetcode/310/step1_improved.py deleted file mode 100644 index c61c305..0000000 --- a/leetcode/310/step1_improved.py +++ /dev/null @@ -1,31 +0,0 @@ -class Solution: - def findMinHeightTrees(self, n: int, edges: list[list[int]]) -> list[int]: - if n == 1: - return [0] - - adjacents = [set() for _ in range(n)] - degrees = [0] * n - for node1, node2 in edges: - adjacents[node1].add(node2) - adjacents[node2].add(node1) - degrees[node1] += 1 - degrees[node2] += 1 - - leaves = [node for node in range(n) if degrees[node] == 1] - remaining = n - while remaining > 2: - next_leaves = [] - for leaf in leaves: - for adj in adjacents[leaf]: - adjacents[adj].remove(leaf) - degrees[adj] -= 1 - if degrees[adj] == 1: - next_leaves.append(adj) - - degrees[leaf] -= 1 - adjacents[leaf].pop() - remaining -= 1 - - leaves = next_leaves - - return leaves From 41bc23021745e2983ab5c56253b1c1dcae7688f0 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Sat, 28 Mar 2026 16:34:12 -0700 Subject: [PATCH 08/13] LeetCode 310: Tweak step 1 TLE solution --- leetcode/310/step1_tle.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/leetcode/310/step1_tle.py b/leetcode/310/step1_tle.py index b786bad..3154464 100644 --- a/leetcode/310/step1_tle.py +++ b/leetcode/310/step1_tle.py @@ -1,3 +1,6 @@ +import collections + + class Solution: def findMinHeightTrees(self, n: int, edges: list[list[int]]) -> list[int]: node_to_adjacents = collections.defaultdict(list) From 9a652f2c6a9dd858d0089c7b81aa90ac4ba5cc8b Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Sat, 28 Mar 2026 17:04:40 -0700 Subject: [PATCH 09/13] LeetCode 310: Add time/space complexity analysis in step 1 --- leetcode/310/memo.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md index a20afa5..90826d4 100644 --- a/leetcode/310/memo.md +++ b/leetcode/310/memo.md @@ -48,4 +48,5 @@ Discord 上で取り組まれた方はいなさそうなので、LeetCode の So やり方だけ頭において、LeetCode 207\. Course Schedule を思い出しながら書いた `step1.py`。 -TODO: Time/Space Complexity +時間計算量: `O(n)`、隣接リストの構築 `O(n)` + 各ノードは高々一回だけ葉として処理される `O(n)` + 各ノードの隣接リストは高々1回しか走査されないため (`adjacents[leaf]`) 合計で `O(n)` +空間計算量: `O(n)`、neighbors が合計で `2(n - 1)`、degrees/leaves/next leaves も `O(n)`。 From 14cb837ff7a1d37259bedd7050ba7e8ce05e4f62 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Sat, 28 Mar 2026 18:15:38 -0700 Subject: [PATCH 10/13] LeetCode 310: Add step 2 solution --- leetcode/310/memo.md | 5 +++++ leetcode/310/step2.py | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 leetcode/310/step2.py diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md index 90826d4..0d91444 100644 --- a/leetcode/310/memo.md +++ b/leetcode/310/memo.md @@ -50,3 +50,8 @@ Discord 上で取り組まれた方はいなさそうなので、LeetCode の So 時間計算量: `O(n)`、隣接リストの構築 `O(n)` + 各ノードは高々一回だけ葉として処理される `O(n)` + 各ノードの隣接リストは高々1回しか走査されないため (`adjacents[leaf]`) 合計で `O(n)` 空間計算量: `O(n)`、neighbors が合計で `2(n - 1)`、degrees/leaves/next leaves も `O(n)`。 + +# Step 2 + +上のポストを見返すと、`neighbors: list[set[int]]` を持ってそれを 隣接ノードの取得 + 長さによる枝の本数チェック、の両方に使用すれば、`neighbors` と `degrees` を別々に持たなくても済んでいる。 +時間・空間計算量が変わることはなく、隣接ノードと持っている枝の本数を一つの変数にまとめることが読みやすさにどれだけ影響があるのか判断がつかないが、とりあえず書いてみることにする -> `step2.py`。 diff --git a/leetcode/310/step2.py b/leetcode/310/step2.py new file mode 100644 index 0000000..c377f5d --- /dev/null +++ b/leetcode/310/step2.py @@ -0,0 +1,24 @@ +class Solution: + def findMinHeightTrees(self, n: int, edges: list[list[int]]) -> list[int]: + if n == 1: + return [0] + + neighbors = [set() for _ in range(n)] + for node1, node2 in edges: + neighbors[node1].add(node2) + neighbors[node2].add(node1) + + remaining = n + leaves = [node for node in range(n) if len(neighbors[node]) == 1] + while remaining > 2: + next_leaves = [] + for leaf in leaves: + neighbor = neighbors[leaf].pop() # only one should be left + neighbors[neighbor].remove(leaf) + if len(neighbors[neighbor]) == 1: + next_leaves.append(neighbor) + remaining -= 1 + + leaves = next_leaves + + return leaves From 4be9eff6d1496d2fadb68ef3afc72962f478a196 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Sat, 28 Mar 2026 22:51:19 -0700 Subject: [PATCH 11/13] LeetCode 310: Add step 3 solution --- leetcode/310/step3.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 leetcode/310/step3.py diff --git a/leetcode/310/step3.py b/leetcode/310/step3.py new file mode 100644 index 0000000..5adc97d --- /dev/null +++ b/leetcode/310/step3.py @@ -0,0 +1,25 @@ +class Solution: + def findMinHeightTrees(self, n: int, edges: list[list[int]]) -> list[int]: + if n == 1: + return [0] + + neighbors = [set() for _ in range(n)] + for node1, node2 in edges: + neighbors[node1].add(node2) + neighbors[node2].add(node1) + + remaining = n + leaves = [node for node in range(n) if len(neighbors[node]) == 1] + while remaining > 2: + next_leaves = [] + + for leaf in leaves: + neighbor = neighbors[leaf].pop() + neighbors[neighbor].remove(leaf) + if len(neighbors[neighbor]) == 1: + next_leaves.append(neighbor) + remaining -= 1 + + leaves = next_leaves + + return leaves From 1701a90fa6d0633a4694705c1b56127f59406f05 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Mon, 30 Mar 2026 20:02:04 -0700 Subject: [PATCH 12/13] LeetCode 310: Add step 4 solution, which is the first attempt of two DFS approach --- .../310/step4_dfs_twice_and_get_middle.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 leetcode/310/step4_dfs_twice_and_get_middle.py diff --git a/leetcode/310/step4_dfs_twice_and_get_middle.py b/leetcode/310/step4_dfs_twice_and_get_middle.py new file mode 100644 index 0000000..c13746c --- /dev/null +++ b/leetcode/310/step4_dfs_twice_and_get_middle.py @@ -0,0 +1,72 @@ +""" +First attempt using two DFS passes to find the longest path (diameter) and return its middle node(s). +I haven't carefully reviewed or refactored yet. +TODO: +- Revisit review comments +- Consider why performing DFS twice can find the longest path +- Improve/refactor this solution. +""" + + +class Solution: + def findMinHeightTrees(self, n: int, edges: list[list[int]]) -> list[int]: + neighbors = [[] for _ in range(n)] + for node1, node2 in edges: + neighbors[node1].append(node2) + neighbors[node2].append(node1) + + def find_farthest( + node: int, visited: list[bool] + ) -> tuple[int, int]: # (farthest node, path length) + path_length = 1 + farthest = node + visited[node] = True + for neighbor in neighbors[node]: + if visited[neighbor]: + continue + + farthest_from_neighbor, path_length_from_neighbor = find_farthest( + neighbor, visited + ) + + if path_length_from_neighbor + 1 > path_length: + path_length = path_length_from_neighbor + 1 + farthest = farthest_from_neighbor + + visited[node] = False + return farthest, path_length + + longest_path_start, _ = find_farthest(0, [False] * n) + longest_path_end, longest_path_length = find_farthest( + longest_path_start, [False] * n + ) + + longest_path = [] + + def traverse(node: int, end: int, visited: list[bool], path: list[int]) -> None: + nonlocal longest_path + + path.append(node) + if node == end: + longest_path = path.copy() + path.pop() + return + + visited[node] = True + + for neighbor in neighbors[node]: + if visited[neighbor]: + continue + traverse(neighbor, end, visited, path) + + path.pop() + visited[node] = False + + traverse(longest_path_start, longest_path_end, [False] * n, []) + + if longest_path_length % 2 == 1: + return [longest_path[longest_path_length // 2]] + else: + return longest_path[ + longest_path_length // 2 - 1 : longest_path_length // 2 + 1 + ] From 4c732a60a40e46aa924c85c8870d3c418c23a660 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Sat, 4 Apr 2026 19:27:30 -0700 Subject: [PATCH 13/13] LeetCode 310: Browsed some web pages about two BFS to find the diameter --- leetcode/310/memo.md | 6 ++++++ leetcode/310/step4_dfs_twice_and_get_middle.py | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/leetcode/310/memo.md b/leetcode/310/memo.md index 0d91444..b873b20 100644 --- a/leetcode/310/memo.md +++ b/leetcode/310/memo.md @@ -55,3 +55,9 @@ Discord 上で取り組まれた方はいなさそうなので、LeetCode の So 上のポストを見返すと、`neighbors: list[set[int]]` を持ってそれを 隣接ノードの取得 + 長さによる枝の本数チェック、の両方に使用すれば、`neighbors` と `degrees` を別々に持たなくても済んでいる。 時間・空間計算量が変わることはなく、隣接ノードと持っている枝の本数を一つの変数にまとめることが読みやすさにどれだけ影響があるのか判断がつかないが、とりあえず書いてみることにする -> `step2.py`。 + +# Step 4 + +[Stack Overflow - Proof of correctness: Algorithm for diameter of a tree in graph theory](https://beta.stackoverflow.com/questions/20010472/proof-of-correctness-algorithm-for-diameter-of-a-tree-in-graph-theory) + +Performing BFS (or DFS) twice to find the farthest node in a tree yields the diameter. diff --git a/leetcode/310/step4_dfs_twice_and_get_middle.py b/leetcode/310/step4_dfs_twice_and_get_middle.py index c13746c..ac2e5fa 100644 --- a/leetcode/310/step4_dfs_twice_and_get_middle.py +++ b/leetcode/310/step4_dfs_twice_and_get_middle.py @@ -3,7 +3,6 @@ I haven't carefully reviewed or refactored yet. TODO: - Revisit review comments -- Consider why performing DFS twice can find the longest path - Improve/refactor this solution. """