-
Notifications
You must be signed in to change notification settings - Fork 0
108 convert sorted array to binary search tree #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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) | ||
| * 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:],
)一つの関心に関係するものがあつまっているほうが、理解するのに必要なワーキングメモリが少なくなるからなんでしょうね。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. left_nums, right_nums を使ってもいいと思います。上はスマホで打ったので横着しました
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. frontier は幅優先探索で未探索の最前線のノードの集合を表す際に使うように思います。深さ優先探索ではあまり使わない印象があります。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 横から失礼します。深さ優先探索でもfrontierを使用している例はあるようで、個人的にはこのままで良いのではないかと思います。 |
||
| 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 | ||
|
|
||
| ``` | ||
| 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:] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PEP8 によると、スライスを作るとき、 なお、このスタイルガイドは“唯一の正解”というわけではなく、数あるガイドラインの一つに過ぎません。チームによって重視される書き方や慣習も異なります。そのため、ご自身の中に基準を持ちつつも、最終的にはチームの一般的な書き方に合わせることをお勧めします。 |
||
| root_node.left = self.sortedArrayToBST(left_nums) | ||
| root_node.right = self.sortedArrayToBST(right_nums) | ||
| return root_node | ||
| 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) |
| 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 |
| 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) |
| 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 |
There was a problem hiding this comment.
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
となり(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oだからざっくりでいいだろうとしていましたが, 厳密に考えると引っかかる点が多そうでしたね。
今後気をつけて使用するようにします。ありがとうございます。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
細かい話は必要な時に引き出しから出せればよいと思います。
見積もりなので、意識的にどこを捨ててるかを分かれば良いのではないでしょうか。