-
Notifications
You must be signed in to change notification settings - Fork 0
108. Convert Sorted Array to Binary Search Tree #26
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 |
|---|---|---|
| @@ -0,0 +1,260 @@ | ||
| # Step1 | ||
|
|
||
| かかった時間:5min | ||
|
|
||
| 計算量:nums.length=Nとして、 | ||
|
|
||
| 時間計算量:O(N) | ||
|
|
||
| 空間計算量:O(NlogN) | ||
|
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. 空間計算量 O(N log N) でしょうか…?
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. 考え方間違ってたらすみません。以下のように考えました。 再帰1回目: k=logNなので、\sum_{i=1}^k (N-i) = kN - \frac{k(k+1)}{2} = NlogN - \frac{logN(logN+1)}{2}
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. あ、減る数が違いますね。。
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. 毎回N-(2^k-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. 再帰 1 回目: N / 2
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. あ、ようやく理解しました。 |
||
|
|
||
| recursive-DFS | ||
| ```python | ||
| # 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 | ||
|
|
||
| root_value_index = len(nums) // 2 | ||
| root = TreeNode(val = nums[root_value_index]) | ||
| root.left = self.sortedArrayToBST(nums[:root_value_index]) | ||
| root.right = self.sortedArrayToBST(nums[root_value_index + 1:]) | ||
|
|
||
| return root | ||
| ``` | ||
| 思考ログ: | ||
| - 直近で解き方を見ている | ||
| - 配列がソートされているので真ん中で割って根から順に木を生やせば良い | ||
| - 例によって再帰なのでスタックを気にしておく | ||
| - 今回はlog(10^4)なので問題なさそう | ||
| - 配列のスライスがうまく機能しているか(漏れたりしてないか)少し脳内テスト | ||
|
|
||
| loop-DFS | ||
| ```python | ||
| # 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() | ||
| node_nums_pairs = [(root, nums)] | ||
| while node_nums_pairs: | ||
| node, subset_nums = node_nums_pairs.pop() | ||
| mid_index = len(subset_nums) // 2 | ||
| node.val = subset_nums[mid_index] | ||
|
|
||
| left_nums = subset_nums[:mid_index] | ||
| if left_nums: | ||
| node.left = TreeNode() | ||
| node_nums_pairs.append((node.left, left_nums)) | ||
| right_nums = subset_nums[mid_index + 1:] | ||
| if right_nums: | ||
| node.right = TreeNode() | ||
| node_nums_pairs.append((node.right, right_nums)) | ||
|
|
||
| return root | ||
| ``` | ||
| 思考ログ: | ||
| - 配列を連れ回しているのは良くないか | ||
| - インデックスで管理すればいいよねという話 | ||
|
|
||
| # Step2 | ||
|
|
||
| 講師役目線でのセルフツッコミポイント: | ||
| - 特に思いつかなかった | ||
|
|
||
| 参考にした過去ログなど: | ||
| - https://github.com/Yoshiki-Iwasa/Arai60/pull/28 | ||
| - midの取り方(left or right)に気が回っていなかった | ||
| - https://github.com/kazukiii/leetcode/pull/25 | ||
| - 確かに先に木を作る方法もあった | ||
| > dummyの値を持ったcomplete binary treeを構築して、inorder traversalしながら値をセットしても良さそう | ||
| - ボックス化 | ||
| - https://ja.wikipedia.org/wiki/%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E5%8C%96 | ||
| - https://github.com/fhiyo/leetcode/pull/26 | ||
| - https://github.com/Mike0121/LeetCode/pull/13 | ||
| - https://github.com/hayashi-ay/leetcode/pull/29 | ||
| - 2分木でもsentinel | ||
| - https://github.com/hayashi-ay/leetcode/pull/29/files#r1593455812 | ||
| - https://github.com/sakupan102/arai60-practice/pull/25 | ||
| - https://github.com/rossy0213/leetcode/pull/13 | ||
| - https://discord.com/channels/1084280443945353267/1183683738635346001/1209195844511858688 | ||
|
|
||
| recursive-DFS(インデックス処理, 右半開区間ver) | ||
| ```python | ||
| # 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 build_bst(left: int, right: int): | ||
| if 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. [質問]
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. あまり深い拘りはないのですが、どこかのコメントでこちらの書き方の方が親和性があるとあったので採用しました。 確かにleft == rightで止まる実装にはなってるんですけど、そこまで読まなくても、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. なるほど、読み手の安心感という観点ですね。自分もこの書き方を使っていこうと思います。 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 > right の場合がないと確定するのは、sortedArrayToBST の定義が終わった時になりますね。しかも、数学的帰納法を利用して考えることになります。 読む人の「心の理論」 パズルを解かせる 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. ありがとうございます、納得しました。 |
||
| return None | ||
|
|
||
| mid = (left + right) // 2 | ||
| node = TreeNode(nums[mid]) | ||
| node.left = build_bst(left, mid) | ||
| node.right = build_bst(mid + 1, right) | ||
| return node | ||
|
|
||
| return build_bst(0, len(nums)) | ||
| ``` | ||
| 思考ログ: | ||
| - 一つ関数を定義する必要があるが、インデックスで処理できるからエコな実装になる | ||
|
|
||
| recursive-DFS(インデックス処理, 閉区間ver) | ||
| ```python | ||
| # 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 build_bst(left: int, right: int): | ||
| if left > right: | ||
| return None | ||
|
|
||
| mid = (left + right) // 2 | ||
| node = TreeNode(nums[mid]) | ||
| node.left = build_bst(left, mid - 1) | ||
| node.right = build_bst(mid + 1, right) | ||
| return node | ||
|
|
||
| return build_bst(0, len(nums) - 1) | ||
| ``` | ||
| 思考ログ: | ||
| - 右半開区間の方が分かりやすいか | ||
|
|
||
| 親と子の情報を積んでいくやり方 | ||
| ```python | ||
| # 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]: | ||
| left, mid, right = 0, len(nums) // 2, len(nums) | ||
|
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. 好みかもしれませんが、3変数横並びだと少し認知負荷あるかと思いました。
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. 確かに演算も入っているし見にくかったかもしれませんね。。 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. 野田さんから以下のようなコメントをもらったことがあるので、ただの代入であればそれぞれで宣言したほうがよいかもです。 |
||
| root = TreeNode(nums[mid]) | ||
| # [(parent_node, index_range_for_child, is_left_child)] | ||
| parent_with_child_infos = [ | ||
| (root, (left, mid), True), | ||
| (root, (mid + 1, right), False) | ||
| ] | ||
| while parent_with_child_infos: | ||
| parent_node, (left, right), is_left = parent_with_child_infos.pop() | ||
| if left >= right: | ||
| continue | ||
| mid = (left + right) // 2 | ||
| child_node = TreeNode(nums[mid]) | ||
| if is_left: | ||
| parent_node.left = child_node | ||
| else: | ||
| parent_node.right = child_node | ||
| parent_with_child_infos.append((child_node, (left, mid), True)) | ||
| parent_with_child_infos.append((child_node, (mid + 1, right), False)) | ||
|
|
||
| return root | ||
| ``` | ||
| 思考ログ: | ||
| - 積む情報が多い、スタックの問題もあるが、再帰は簡潔に書けて良い | ||
|
|
||
| 先に木を作ってin-orderで値を埋めていく方法 | ||
| ```python | ||
| from collections import deque | ||
|
|
||
|
|
||
| # 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]: | ||
| # complete binary tree | ||
|
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. コメントで関数名の補足わかりやすいです。 |
||
| def build_cbt(index: int): | ||
| if index >= len(nums): | ||
| return None | ||
| root = TreeNode() | ||
| root.left = build_cbt(2 * index + 1) | ||
| root.right = build_cbt(2 * index + 2) | ||
| return root | ||
|
|
||
| nums_queue = deque(nums) | ||
| def set_values(cbt_root: Optional[TreeNode]) -> None: | ||
| nodes = [cbt_root] | ||
| while nodes: | ||
|
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. これnodesとwhileいらなくないでしょうか?
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. ご明察です。 書き直しました。 # 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]:
# complete binary tree
def build_cbt(index: int):
if index >= len(nums):
return None
root = TreeNode()
root.left = build_cbt(2 * index + 1)
root.right = build_cbt(2 * index + 2)
return root
nums_queue = deque(nums)
def set_values(cbt_root: Optional[TreeNode]) -> None:
if not cbt_root:
return None
set_values(cbt_root.left)
cbt_root.val = nums_queue.popleft()
set_values(cbt_root.right)
cbt_root = build_cbt(0)
set_values(cbt_root)
return cbt_root |
||
| node = nodes.pop() | ||
| if not node: | ||
| continue | ||
| set_values(node.left) | ||
| node.val = nums_queue.popleft() | ||
| set_values(node.right) | ||
|
|
||
| cbt_root = build_cbt(0) | ||
| set_values(cbt_root) | ||
|
|
||
| return cbt_root | ||
| ``` | ||
| 思考ログ: | ||
| - in-orderを書く機会が少ないのでせっかくなので書いておく | ||
| - https://github.com/kazukiii/leetcode/pull/25/files | ||
| - nonlocalを使わない形に少しアレンジ | ||
|
|
||
| # Step3 | ||
|
|
||
| かかった時間:3min | ||
|
|
||
| ```python | ||
| # 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 | ||
|
|
||
| root = TreeNode() | ||
| node_range_pairs = [(root, (0, len(nums)))] | ||
|
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とrightはまとめなくても良いかもです。 |
||
| while node_range_pairs: | ||
| node, (left, right) = node_range_pairs.pop() | ||
| mid = (left + right) // 2 | ||
| node.val = nums[mid] | ||
|
|
||
| if left < mid: | ||
| node.left = TreeNode() | ||
| node_range_pairs.append((node.left, (left, mid))) | ||
| if mid + 1 < right: | ||
| node.right = TreeNode() | ||
| node_range_pairs.append((node.right, (mid + 1, right))) | ||
|
|
||
| return root | ||
| ``` | ||
| 思考ログ: | ||
| - 再帰でいい気がするが、スタック版を練習も兼ねて | ||
|
|
||
| # Step4 | ||
|
|
||
| ```python | ||
| ``` | ||
| 思考ログ: | ||
|
|
||
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(n/2)のコピーコストが発生して、O(n log n)になりませんか?
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(NlogN)で空間がO(N)でしょうか?
#26 (comment)
ちょっと考えます。
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.
コピーコストの部分は
#26 (comment)
と同様、
で、 O(N) だと思いました。
Uh oh!
There was an error while loading. Please reload this page.
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.
上のコメント間違えました。 @kazukiii さんのおっしゃる通り、各レベル k で N / 2^k * 2^k = N のコピーコストが発生し、レベルの数が log N なので、O(N log N) だと思います。
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.
纏めると、
時間計算量:0(NlogN)
空間計算量:0(N)
インデックス管理方式にすると
時間計算量:0(N)
空間計算量:0(logN)
ですかね。