Skip to content
Open
Show file tree
Hide file tree
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
186 changes: 186 additions & 0 deletions memo.md
Original file line number Diff line number Diff line change
@@ -1 +1,187 @@
# Step1

## アプローチ (14:22)

* rootとなる値がわかれば, それより左側と右側で再帰を呼ぶことで解ける
* rootとなる値は中央値?
* height-balancedではある
* rootの左側と右側の個数が等しくなるようにすればbalancedにはなるよな
* 日本語で仕事を考えてみる
* 与えられた数字の列からrootになる値を決める(自分の仕事)
* 決めた数字の左側と右側の配列を部下に渡して, 自分が繋ぐべきrootをもらう
* rootを返す
* 計算量を考える
* 計算量をT(N)とする
* T(N) = T(N/2) + T(N/2) = T(N / 2^2) + ... + T(N / 2^2)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[fyi] 細かい話ですが、rootノードの作成や部分配列の中央値へのアクセスなど定数時間の処理が含まれるため、漸化式は以下のようになります。
$$T(N) = 2T(N/2) + O(1)$$

最終的なオーダー $T(N) = O(N)$ は変わりませんが、代入法で上界を証明する際には注意が必要です。
$T(N)\le cN$ なる仮定を代入すると、
$$T(N) \le cN + d$$
となり( $d$$O(1)$ の係数)、 $cN+d \le cN$ なる 定数 $c$ が存在せずに証明が回りません。代わりに定数 $c_0, c_1$ を使ったより強い評価
$T(N) \le c_1 N- c_0$ の仮定が必要です。 cf

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Oだからざっくりでいいだろうとしていましたが, 厳密に考えると引っかかる点が多そうでしたね。
今後気をつけて使用するようにします。ありがとうございます。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

細かい話は必要な時に引き出しから出せればよいと思います。
見積もりなので、意識的にどこを捨ててるかを分かれば良いのではないでしょうか。

* T(N) = T(1) * 2 * 2^(log2_N)
* O(N)
* 追記: slicingを使っていた場合はO(NlogN)になる
* 空間計算量
* 再帰の最大回数 ... logN
* 結果として使う分 ... size(TreeNode) * N
* O(N)

## Code1-1 (Recursion) - 1:49

```python
from typing import List, Optional


# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if not nums:
return None
if len(nums) == 1:
return TreeNode(nums[0])

mid_idx = len(nums) // 2
root_node = TreeNode(nums[mid_idx])
left_nums = nums[:mid_idx]
right_nums = nums[mid_idx + 1:]
root_node.left = self.sortedArrayToBST(left_nums)
root_node.right = self.sortedArrayToBST(right_nums)
return root_node
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

「ルートノードの左右の子を一旦仮り置き(None)で作ります。部下に仕事を渡します。結果からルートノードの左右の子を上書きして返します。」
よりかは、
「部下に作成をお願いした木の根を左右の子に、中央値を値にしたノードを根として作成して返します。」
の方が個人的にはわかりやすいと思います

return TreeNode(
    val=nums[mid_index],
    left=self.sortedArrayToBST(nums[:mid_index],
    right=self.sortedArrayToBST(nums[mid_index + 1:],
)

一つの関心に関係するものがあつまっているほうが、理解するのに必要なワーキングメモリが少なくなるからなんでしょうね。
もちろん集めて肥大化したら理解しにくくなるので、トレードオフを見ることも必要ではあると思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

left_nums, right_nums を使ってもいいと思います。上はスマホで打ったので横着しました

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

自分もそちらの方がわかりやすいと思いました。


```

# Step2

## 他の人のコード

* https://github.com/mamo3gr/arai60/pull/23/files
* whileを使用した再帰
* 区間を指定した再帰関数を作った実装もある

## Code2-1 (Recursion)

```python
from typing import List, Optional


# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
def sortedArrayToBST_with_range(nums, left, right):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

loweCamel と lower_snake が混ざっている点に違和感を感じました。自分なら build 等、シンプルな関数名を付けると思います。

if left > right:
return None
if left == right:
return TreeNode(nums[left])

mid_idx = (left + right) // 2
return TreeNode(
val=nums[mid_idx],
left=sortedArrayToBST_with_range(nums, left, mid_idx - 1),
right=sortedArrayToBST_with_range(nums, mid_idx + 1, right)
)

return sortedArrayToBST_with_range(nums, 0, len(nums) - 1)


```

## Code2-2 (DFS)

```python
from typing import List, Optional


# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
root = TreeNode()
frontier = [(0, len(nums) - 1, root)]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

frontier は幅優先探索で未探索の最前線のノードの集合を表す際に使うように思います。深さ優先探索ではあまり使わない印象があります。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

横から失礼します。深さ優先探索でもfrontierを使用している例はあるようで、個人的にはこのままで良いのではないかと思います。
https://github.com/aimacode/aima-python/blob/master/search.py#L206

while frontier:
left, right, node = frontier.pop()
mid = (left + right) // 2
node.val = nums[mid]
if left <= mid - 1:
node.left = TreeNode()
frontier.append((left, mid - 1, node.left))
if mid + 1 <= right:
node.right = TreeNode()
frontier.append((mid + 1, right, node.right))
return root

```

# Step3

## Code3-1 (Recursion)

```python
# 1st: 1:41
# 1st: 1:19
# 1st: 1:12


# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: list[int]) -> TreeNode | None:
def sortedArrayToBST_with_range(nums, left, right):
if left > right:
return None
if left == right:
return TreeNode(nums[left])

mid = (left + right) // 2
return TreeNode(
val=nums[mid],
left=sortedArrayToBST_with_range(nums, left, mid - 1),
right=sortedArrayToBST_with_range(nums, mid + 1, right)
)
return sortedArrayToBST_with_range(nums, 0, len(nums) - 1)

```

## Code3-2 (DFS)

```python
# 1st: 1:55
# 1st: 1:41
# 1st: 1:25

# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: list[int]) -> TreeNode | None:
root = TreeNode()
frontier = [(0, len(nums) - 1, root)]
while frontier:
left, right, node = frontier.pop()
mid = (left + right) // 2
node.val = nums[mid]
if left <= mid - 1:
node.left = TreeNode()
frontier.append((left, mid - 1, node.left))
if mid + 1 <= right:
node.right = TreeNode()
frontier.append((mid + 1, right, node.right))

return root

```
23 changes: 23 additions & 0 deletions step1-1_recursion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import List, Optional


# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if not nums:
return None
if len(nums) == 1:
return TreeNode(nums[0])

mid_idx = len(nums) // 2
root_node = TreeNode(nums[mid_idx])
left_nums = nums[:mid_idx]
right_nums = nums[mid_idx + 1:]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

PEP8 によると、スライスを作るとき、 + 演算子の両側にはスペースを空けないことになっているようです。
https://peps.python.org/pep-0008/#pet-peeves

なお、このスタイルガイドは“唯一の正解”というわけではなく、数あるガイドラインの一つに過ぎません。チームによって重視される書き方や慣習も異なります。そのため、ご自身の中に基準を持ちつつも、最終的にはチームの一般的な書き方に合わせることをお勧めします。

root_node.left = self.sortedArrayToBST(left_nums)
root_node.right = self.sortedArrayToBST(right_nums)
return root_node
25 changes: 25 additions & 0 deletions step2-1_recursion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import List, Optional


# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
def sortedArrayToBST_with_range(nums, left, right):
if left > right:
return None
if left == right:
return TreeNode(nums[left])

mid_idx = (left + right) // 2
return TreeNode(
val=nums[mid_idx],
left=sortedArrayToBST_with_range(nums, left, mid_idx - 1),
right=sortedArrayToBST_with_range(nums, mid_idx + 1, right)
)

return sortedArrayToBST_with_range(nums, 0, len(nums) - 1)
24 changes: 24 additions & 0 deletions step2-2_dfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import List, Optional


# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
root = TreeNode()
frontier = [(0, len(nums) - 1, root)]
while frontier:
left, right, node = frontier.pop()
mid = (left + right) // 2
node.val = nums[mid]
if left <= mid - 1:
node.left = TreeNode()
frontier.append((left, mid - 1, node.left))
if mid + 1 <= right:
node.right = TreeNode()
frontier.append((mid + 1, right, node.right))
return root
26 changes: 26 additions & 0 deletions step3-1_recursion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 1st: 1:41
# 1st: 1:19
# 1st: 1:12


# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: list[int]) -> TreeNode | None:
def sortedArrayToBST_with_range(nums, left, right):
if left > right:
return None
if left == right:
return TreeNode(nums[left])

mid = (left + right) // 2
return TreeNode(
val=nums[mid],
left=sortedArrayToBST_with_range(nums, left, mid - 1),
right=sortedArrayToBST_with_range(nums, mid + 1, right)
)
return sortedArrayToBST_with_range(nums, 0, len(nums) - 1)
26 changes: 26 additions & 0 deletions step3-2_dfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 1st: 1:55
# 1st: 1:41
# 1st: 1:25

# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: list[int]) -> TreeNode | None:
root = TreeNode()
frontier = [(0, len(nums) - 1, root)]
while frontier:
left, right, node = frontier.pop()
mid = (left + right) // 2
node.val = nums[mid]
if left <= mid - 1:
node.left = TreeNode()
frontier.append((left, mid - 1, node.left))
if mid + 1 <= right:
node.right = TreeNode()
frontier.append((mid + 1, right, node.right))

return root