Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions leetcode/arai60/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# 108. Convert Sorted Array to Binary Search Tree
* 問題: https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/
* 言語: Python

# Step1
* 昇順の数値を持つ配列から、高さのバランスが取れた(height-balancedな)二分探索木(BST)を構築
- BSTとは以下の条件を満たす木
- 左の部分木(subtree)のすべてのノードの値はルートの値より小さい
- 右の部分木(subtree)のすべてのノードの値はルートの値より大きい
- 高さバランスのとれたBSTとは、各ノードの左右の部分木の高さ(深さ)の差が1以下の木。これにより、検索、挿入、削除などの操作が効率的な $O(\log n)$ になる。
* 昇順に並んでいる性質を利用して配列の中央の値を起点にして木を構築する方針を立てたが、5分経っていたので正答をみる

## 正答
* 深さ優先探索(DFS)
* 配列(部分配列)の中央の値からルート(根)ノードを決定
* 上記ロジックを再帰的に左半分と右半分に適用
* 配列の長さをnとすると:
- 左部分木:$⌊n/2⌋$ 個の要素
- 右部分木:$n - ⌊n/2⌋ - 1$ 個の要素
```py
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if not nums:
return None

mid = len(nums) // 2
root = TreeNode(nums[mid])

root.left = self.sortedArrayToBST(nums[:mid])
root.right = self.sortedArrayToBST(nums[mid+1:])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

root.right = self.sortedArrayToBST(nums[mid + 1:])

とスライスのところスペースを開けるべきかとおもったのですがこのあたり全部許容なんですね。
https://peps.python.org/pep-0008/#pet-peeves

# Correct:
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]


return root
```
* 時間計算量: $O(n \log n)$
* スタックを使った方法でも書けそう?

# Step2
## 他人のコードを読む
* 典型コメント: https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.9s63li7jygve
- > 気になって調べてみたら一次元のものはmiddle、二次元のものにはcenterを使う感覚があるみたいです。
- ref. https://github.com/colorbox/leetcode/pull/38#discussion_r1999900824
* https://github.com/colorbox/leetcode/pull/38
- C++
- DFS
- `108/step2_3.cpp` は、再帰ではなくスタックを使い、ポインタへのポインタ(pointer to pointer)による解法
- ポインタへのポインタとは、ポインタのアドレスを格納するポインタ
- Pythonばかり書いているので、C++ではオブジェクトの動的な生成ではオブジェクトを確保した位置のアドレスを返すことを明確に意識しないといけないことを忘れていた
- `new TreeNode()` はTreeNodeオブジェクトのアドレスを返す
- スタックに親ノードのポインタ、左インデックス、右インデックスのタプルを積んでいく
* https://github.com/irohafternoon/LeetCode/pull/27
- C++
- DFS
* `TreeNode`の `left` と `right` 、配列インデックスの `left` と `right` で一瞬混乱してしまう
- 後者を `left_i` 、 `right_i` とするのもあり?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

start, stop にするのもありだと思いました。

* https://github.com/ichika0615/arai60/pull/17
- Python
- DFSと幅優先探索(BFS)
- BFSの場合、キューを使って木のレベル(高さ)ごとにノードを追加していく
- 自分で概算をして「手の運動」をする
- ref. https://github.com/ichika0615/arai60/pull/17#discussion_r2019099632
- ref. https://github.com/ichika0615/arai60/pull/17/files#r2020138924
- 時間計算量: $O(n \log n)$

$$
\begin{align*}
T(n) &= 2T(n/2) + cn \\
&= 2[2T(n/4) + cn/2] + cn \\
&= 4T(n/4) + cn + cn \\
&= 4T(n/4) + 2cn \\
&= 4[2T(n/8) + cn/4] + 2cn \\
&= 8T(n/8) + cn + 2cn \\
&= 8T(n/8) + 3cn \\
&... \\
&= 2^k T(n/2^k) + k×cn \\
\end{align*}
$$

* 終了条件: $n/2^k = 1$ のとき(つまり $k = \log_2 n$ )

$$
\begin{align*}
T(n) &= 2^{\log_2 n} × T(1) + cn × \log_2 n \\
&= n × T(1) + cn × \log_2 n \\
&= O(n) + O(n \log n) \\
&= O(n \log n)
\end{align*}
$$

* 直観的には、木の高さごとの $n$ 回の走査×再帰の深さ $\log n$ で $n\log n$

# Step3
* 再帰DFS
* スライスではなくインデックスを使う方法
```py
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
def helper(left, right):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あまり外部での利用を想定していないのでよいかもしれませんが、sorted_array_to_bst_helper くらいの名前でもよいのかなと思いました。

if left > right:
return None

mid = (left + right) // 2
root = TreeNode(nums[mid])
root.left = helper(left, mid - 1)
root.right = helper(mid + 1, right)

return root

return helper(0, len(nums) - 1)
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

読みやすかったです。

* 解答時間
- 1回目: 1:53
- 2回目: 1:50
- 3回目: 1:45