-
Notifications
You must be signed in to change notification settings - Fork 0
105. Construct Binary Tree from preorder and inorder traversal #37
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,399 @@ | ||
| ### Step1 | ||
|
|
||
| - search_parent_of_right_childは入力のcandidatesの要素を変更してしまうが、これはまあしょうがない気がする(実際候補は変更するし、、、) | ||
| - ちょっと遅めだったが、nodeからindexの連想配列とか作ると速くなるかなあと何となく思う | ||
| - メモリは余分にかかるけどね | ||
| - search_parent_of_right_childのprev_nodeというのはちょっとスッキリしないが代案はない | ||
|
|
||
| ```python | ||
| python | ||
| class Solution: | ||
| def is_inorder(self, node, next_node, inorder): | ||
| for i in range(len(inorder)): | ||
| if inorder[i] == node.val: | ||
| node_index = i | ||
| if inorder[i] == next_node.val: | ||
| next_node_index = i | ||
| return node_index < next_node_index | ||
|
|
||
| def search_parent_of_right_child(self, child_node, candidates, inorder): | ||
| prev_node = None | ||
| while True: | ||
| if not candidates: | ||
| return prev_node | ||
| node_searched = candidates.pop() | ||
| if not self.is_inorder(node_searched, child_node, inorder): | ||
| candidates.append(node_searched) | ||
|
Comment on lines
+24
to
+26
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. pop して append し直しているのは、動作として余計に感じます。
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. そうですね、以前もsetで2回入れてる(結果的にsetだからOK)のが気になるというレビューをいただいたことがあり、冗長な操作をうっかりしてしまう癖がありそうなので気をつけます |
||
| return prev_node | ||
| prev_node = node_searched | ||
|
|
||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
| assert len(preorder) == len(inorder) | ||
| assert len(preorder) == len(set(preorder)) | ||
| assert len(inorder) == len(set(inorder)) | ||
| root = TreeNode(preorder[0]) | ||
| parent_candidates = [root] | ||
| for i in range(1, len(preorder)): | ||
| child_node = TreeNode(preorder[i]) | ||
| if not self.is_inorder(parent_candidates[-1], child_node, inorder): | ||
| parent_node = parent_candidates[-1] | ||
| parent_node.left = child_node | ||
| parent_candidates.append(child_node) | ||
| continue | ||
| parent_node = self.search_parent_of_right_child(child_node, parent_candidates, inorder) | ||
| parent_node.right = child_node | ||
| parent_candidates.append(child_node) | ||
| return 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. https://discord.com/channels/1084280443945353267/1247673286503039020/1300957769477918791 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 の場合は、ぶら下げる先のノードの inorder における位置、right の場合は、ぶら下げる先のノードのスタック上の一つ上のノードの inorder における位置なのだけれども、これを分かりやすく説明するのが大変です。 速度の面では、こちらのほうの map や unordered_map のアクセスが増えて遅くなる要素が、あちらのほうの tuple が stack に載って遅くなる要素よりも、強く効くかしら。 class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
map<int, int> inorder_position;
for (int i = 0; i < inorder.size(); i++) {
inorder_position[inorder[i]] = i;
}
// contains all nodes that may have a child.
vector<TreeNode*> stack;
TreeNode* root = new TreeNode(preorder[0]);
stack.push_back(root);
for (int i = 1; i < preorder.size(); i++) {
TreeNode* node = new TreeNode(preorder[i]);
int node_position = inorder_position[node->val];
TreeNode* back = stack.back();
if (node_position < inorder_position[back->val]) {
back->left = node;
stack.push_back(node);
continue;
}
stack.pop_back();
while (!stack.empty()) {
TreeNode* parent = stack.back();
if (node_position < inorder_position[parent->val]) {
break;
}
stack.pop_back();
back = parent;
}
back->right = node;
stack.push_back(node);
}
return root;
}
};
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_limit, right_limitの方が、やりたい操作に対して直接的な変数を定義してますね |
||
|
|
||
| ## Step2 | ||
|
|
||
| https://github.com/TORUS0818/leetcode/pull/31/files | ||
|
|
||
| [https://github.com/seal-azarashi/leetcode/pull/29](https://github.com/seal-azarashi/leetcode/pull/29#pullrequestreview-2344592484) | ||
|
|
||
| https://github.com/goto-untrapped/Arai60/pull/53 | ||
|
|
||
| https://github.com/Ryotaro25/leetcode_first60/pull/31 | ||
|
|
||
| https://github.com/hayashi-ay/leetcode/pull/43 | ||
|
|
||
| - 再帰でやる方法は思いつかなかった | ||
|
|
||
| - 再帰で、子を呼び出す前に全部の処理を終える方法(ややこしいのでこうは書かないが練習のために)、さらにスライスでコピーをしないためにindexで管理 | ||
| - .right, .leftの後処理さえしないために、引き継ぎに必要な情報は以下3つ | ||
| - 今のtree size | ||
| - 今のtreeで、preorderの最初のところ | ||
| - 今のtreeで、inorderの最初のところ | ||
| - 子を呼び出す前に処理するには、必要な情報は引数として渡す必要がある(returnでは渡せない) | ||
| - どこまで引数で渡し、どこまでreturnで渡すか、その選択の基準を自分の中でまだ持てていない | ||
| - listの[indexメソッド](https://docs.python.org/3/tutorial/datastructures.html)知らなかったのでドキュメント読んだ | ||
| - 複数ある場合は最初の値 | ||
| - ない場合はValueError | ||
| - start, endのオプションがあり、その範囲内での相対indexを返す | ||
|
|
||
| ```python | ||
| from dataclasses import dataclass | ||
|
|
||
| @dataclass | ||
| class SubtreeIndexRange: | ||
| subtree_size: int | ||
| preorder_min_index: int | ||
| inorder_min_index: int | ||
|
|
||
| class Solution: | ||
| def calculate_child_index_range(self, parent_subtree: SubtreeIndexRange, parent_index_inorder: int) -> tuple: | ||
| left_tree_size = parent_index_inorder - parent_subtree.inorder_min_index | ||
| left_preorder_min_index = parent_subtree.preorder_min_index + 1 | ||
| left_inorder_min_index = parent_subtree.inorder_min_index | ||
| left_subtree = SubtreeIndexRange(left_tree_size, left_preorder_min_index, left_inorder_min_index) | ||
| right_tree_size = parent_subtree.subtree_size - left_tree_size - 1 | ||
| right_preorder_min_index = parent_subtree.preorder_min_index + left_tree_size + 1 | ||
| right_inorder_min_index = parent_subtree.inorder_min_index + left_tree_size + 1 | ||
| right_subtree = SubtreeIndexRange(right_tree_size, right_preorder_min_index, right_inorder_min_index) | ||
| return left_subtree, right_subtree | ||
|
|
||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
|
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. 必要以上に複雑な処理になっている印象を受けました。一度 inorder, preorder それぞれの配列の特徴と、最低限必要なデータが何かを整理して解法を考え直すと良いかもしれません。
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. コメントありがとうございます。すみません🙏、Step4の最終コードとコメントを見て理解できなかったのでまずは以下の疑問を解決したいのですが、 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. @nittoco
preorder 配列のいずれかの要素を指すインデックスです。初期値が0で、buildTreeHelper() が呼ばれる度に値がインクリメント (一つ右に移動) されます。
申し訳ありません、inorder と preorder を書き間違えていましたので訂正させてください。
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. @seal-azarashi
|
||
| assert len(preorder) == len(inorder) | ||
| assert len(preorder) == len(set(preorder)) | ||
| assert len(inorder) == len(set(inorder)) | ||
| preorder_index_to_node = [] | ||
| for val in preorder: | ||
| preorder_index_to_node.append(TreeNode(val)) | ||
| node_val_to_inorder_index = {val: i for i, val in enumerate(inorder)} | ||
| def build_tree_helper(subtree: SubtreeIndexRange): | ||
| if subtree.subtree_size <= 1: | ||
| return None | ||
| parent_node = preorder_index_to_node[subtree.preorder_min_index] | ||
| parent_index_inorder = node_val_to_inorder_index[parent_node.val] | ||
| left_subtree, right_subtree = self.calculate_child_index_range(subtree, parent_index_inorder) | ||
| if left_subtree.subtree_size: | ||
| parent_node.left = preorder_index_to_node[left_subtree.preorder_min_index] | ||
| if right_subtree.subtree_size: | ||
| parent_node.right = preorder_index_to_node[right_subtree.preorder_min_index] | ||
| build_tree_helper(left_subtree) | ||
| build_tree_helper(right_subtree) | ||
| build_tree_helper(SubtreeIndexRange(len(preorder), 0, 0)) | ||
| return preorder_index_to_node[0] | ||
|
|
||
| ``` | ||
|
|
||
| - dataclassのドキュメントと、ソースコード(https://github.com/python/cpython/blob/main/Lib/dataclasses.py)を読んだ | ||
| - process_class(L930)で__init__とか__repr__みたいな特殊メソッドを定義して、クラスを作っているみたい。主な関数はこんな感じ? | ||
| - def init_fn(L614) | ||
| - 関数の初期化。init_paramとfield_initが呼ばれている。前者はx:int=3のような__init__関数のパラメータ文字列、後者はself.x = 1のような__init__関数の中身の文字列が作られる。その後、add_fnを__init__関数で実行 | ||
| - FuncBuilder.add_fn | ||
| - self.namesに関数名を、self.srcに関数のコードの文字列を入れる | ||
| - *FuncBuilder.*add_fns_to_class | ||
| - dataclassで作る、クラスの中の関数の一覧を返す関数(create_fn)を、文字列からexecで実行することで、name属性に関数を結びつける | ||
| - add_fnでsrcに関数一覧の中身のコードが入っているのでそれを中に置いて、return self.namesで関数名を返すことで、関数create_fnを作っている | ||
|
|
||
|
|
||
| https://github.com/fhiyo/leetcode/pull/31 | ||
|
|
||
| - 再帰で呼び出す関数の引数に必要な計算を、あらかじめ別変数に入れてその変数を引数に、とやらずに、関数の引数の中で計算式を入れる選択肢がある。いつも忘れがち。 | ||
| - 引数が何を示すかわかりにくくなるデメリットはあるかも? | ||
| - Step1のように、preorder順に見ていくが、rootからいちいち親を探す方法を実装 | ||
| - いちいちrootに戻って探すのは手間な感じもするが、実装としては素直なのかもしれない | ||
| - 実行時間は結構かかる | ||
| - うーん、search_parent関数の中身ネストが深いが、代案が思いつかない | ||
|
|
||
| ```python | ||
|
|
||
| class Solution: | ||
| def search_parent(self, child, root, node_val_to_inorder_index): | ||
|
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. 開発チームの方針や好みにもよりますが、buildTree() には型ヒントが付与されているので、この場合はこちらにも付与してあげると良いのではないでしょうか。
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. そうですね、型ヒントに慣れていないのでLeetCodeのデフォルトの分だけつけてましたが、あった方が親切ですね |
||
| result = root | ||
| while True: | ||
| if node_val_to_inorder_index[child.val] == node_val_to_inorder_index[result.val]: | ||
| raise ValueError("The node is already in tree") | ||
| if node_val_to_inorder_index[child.val] < node_val_to_inorder_index[result.val]: | ||
| if not result.left: | ||
| return result, "left" | ||
| result = result.left | ||
| else: | ||
| if not result.right: | ||
| return result, "right" | ||
| result = result.right | ||
|
|
||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
| assert len(preorder) == len(inorder) | ||
| assert len(preorder) == len(set(preorder)) | ||
| assert len(inorder) == len(set(inorder)) | ||
| root = TreeNode(preorder[0]) | ||
| node_val_to_inorder_index = {val: i for i, val in enumerate(inorder)} | ||
| for i in range(1, len(preorder)): | ||
| node = TreeNode(preorder[i]) | ||
| parent, direction = self.search_parent(node, root, node_val_to_inorder_index) | ||
| if direction == "left": | ||
| parent.left = node | ||
| else: | ||
| parent.right = node | ||
|
Comment on lines
+167
to
+170
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. これ、search_parent の中に入れてしまってもいい気がします。 |
||
| return root | ||
| ``` | ||
|
|
||
| - 再帰をstackに直す | ||
| - nullのnodeも突っ込んで後で取り出す時にチェックするより、どうせ.left, .rightで繋げる時に本当にあるかチェックしなきゃいけないので、stackにpushするかもその中に入れれば、取り出す時のチェックはいらない | ||
|
|
||
| ```python | ||
|
|
||
| class Solution: | ||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
| assert len(preorder) == len(inorder) | ||
| assert len(preorder) == len(set(preorder)) | ||
| assert len(inorder) == len(set(inorder)) | ||
|
Comment on lines
+181
to
+183
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. Step 2 の中で、このような不正な値のチェックがあったりなかったりするのが気になりました。どういった意図でチェックする or しないようにしているのか知りたいです!
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. すみません、最後の方は単純に書き忘れてただけです。 |
||
| root = TreeNode(preorder[0]) | ||
| stack = [(root, preorder, inorder)] | ||
|
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. Tuple だと各要素がどのような役割を持っているのか理解するのが少し大変なので、個人的には別のデータ構造を使ってほしい気持ちになりました。この場合そのままイミュータブルではあって欲しいので、NamedTuple や frozen 属性が付与されたdataclass あたりだと嬉しいです。特に後者は型ヒントも付与出来るので、実際のプロダクトで使われているとより安心感が強いですね。 |
||
| while stack: | ||
| parent, node_vals_preorder, node_vals_inorder = stack.pop() | ||
| left_count = node_vals_inorder.index(parent.val) | ||
| right_count = len(node_vals_preorder) - left_count - 1 | ||
| if left_count: | ||
| parent.left = TreeNode(node_vals_preorder[1]) | ||
| stack.append( | ||
| ( | ||
| parent.left, | ||
| node_vals_preorder[1 : left_count + 1], | ||
| node_vals_inorder[:left_count], | ||
|
Comment on lines
+195
to
+196
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. 自分もStackで配列を丸ごと持つのはちょっと気になりました。範囲のindexを持つ方がメモリ使用量的には良いですかね、とはいえ配列で操作する方が処理は簡単に書けそうなのと、stackには多くても
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. そうですね、Index持つのだと実装が複雑なので、改善するならnumpyとか使ってviewにするかという感じですかね |
||
| ) | ||
| ) | ||
| if right_count: | ||
| parent.right = TreeNode(node_vals_preorder[-right_count]) | ||
| stack.append( | ||
| ( | ||
| parent.right, | ||
| node_vals_preorder[-right_count:], | ||
| node_vals_inorder[-right_count:], | ||
| ) | ||
| ) | ||
| return root | ||
| ``` | ||
|
|
||
| - 上と同じロジックの再帰 | ||
|
|
||
| ```python | ||
|
|
||
| class Solution: | ||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
| def build_tree_helper(parent, node_val_preorder, node_val_inorder): | ||
| left_node_count = node_val_inorder.index(parent.val) | ||
| right_node_count = len(node_val_preorder) - left_node_count - 1 | ||
| if left_node_count: | ||
| parent.left = TreeNode(node_val_preorder[1]) | ||
| build_tree_helper(parent.left, node_val_preorder[1:left_node_count + 1], node_val_inorder[:left_node_count]) | ||
| if right_node_count: | ||
| parent.right = TreeNode(node_val_preorder[-right_node_count]) | ||
| build_tree_helper(parent.right, node_val_preorder[-right_node_count:], node_val_inorder[-right_node_count:]) | ||
| root = TreeNode(preorder[0]) | ||
| build_tree_helper(root, preorder, inorder) | ||
| return root | ||
| ``` | ||
|
|
||
| - nodeを返して、nodeを繋げるのは親がやることにした再帰 | ||
| - これが簡潔で一番好き | ||
| - if left_node_count: の条件はつけず、一番上でif not preorder: returnをやるのは、stackのノードに空のも突っ込んで後でチェックするのに対応するのか(今更気づいた) | ||
|
|
||
| ```python | ||
|
|
||
| class Solution: | ||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
| root = TreeNode(preorder[0]) | ||
| left_node_count = inorder.index(root.val) | ||
| right_node_count = len(preorder) - left_node_count - 1 | ||
| if left_node_count: | ||
| root.left = self.buildTree(preorder[1:left_node_count + 1], inorder[:left_node_count]) | ||
| if right_node_count: | ||
| root.right = self.buildTree(preorder[-right_node_count:], inorder[-right_node_count:]) | ||
|
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. ただの感想ですが、Python だと negative index で指定が出来るのですね。勉強になりました。 |
||
| return root | ||
| ``` | ||
|
|
||
| ## Step4(追加のやり方) | ||
| - [この辺](https://discord.com/channels/1084280443945353267/1247673286503039020/1300957769477918791) を参照 | ||
| - inorder順に見ていく方法 | ||
| - 最初わけわかんなくなった(下が訳わかんなくなってエラーが出たコード) | ||
| - 各ループの時点で、stackにどこのnodeがどの順序で入っていれば良いのか混乱していた | ||
|
|
||
| ```python | ||
| # 訳わかんなくなってエラーが出たコード | ||
|
|
||
| # inorer順に見てく書き方 | ||
| class Solution: | ||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
| # 入力値のチェック 今回は省略 | ||
| stack = [] | ||
|
|
||
| # inorderで今までのnodeの中で、.rightがまだのものを繋げる関数 | ||
| def connect_right_child_nodes_so_far(node): | ||
| child = None | ||
| while stack: | ||
| parent = stack[-1] | ||
| if parent == node: | ||
| return child | ||
| parent.right = child | ||
| child = stack.pop() | ||
|
|
||
| node_val_to_preorder_index = {val: i for i, val in enumerate(preorder)} | ||
| prev_val = None | ||
| dummy = TreeNode() | ||
| root = None | ||
| for val in inorder: | ||
| node = TreeNode(val) | ||
| stack.append(node) | ||
| if prev_val is None: | ||
| prev_val = val | ||
| continue | ||
| if node_val_to_preorder_index[prev_val] < node_val_to_preorder_index[val]: | ||
| prev_val = val | ||
| continue | ||
| if node_val_to_preorder_index[val] == 0: | ||
| root = node | ||
| dummy.left = root | ||
| node.left = connect_right_child_nodes_so_far(node) | ||
| prev_val = val | ||
| return connect_right_child_nodes_so_far(dummy) | ||
| ``` | ||
|
|
||
| - かなり時間がかかったが、なんとか書けた、苦労した | ||
| - stackに入っているデータの不変条件をよく意識 | ||
| - う〜ん、スッキリするような、しないような??? | ||
|
|
||
| ```python | ||
| class Solution: | ||
| dummy_root_preorder_index = -1 | ||
| dummy_root_val = 0 | ||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
| stack = [] | ||
| node_val_to_preorder_index = {val: i for i, val in enumerate(preorder)} | ||
| dummy_root = None | ||
| inorder.append(self.dummy_root_val) | ||
| node_val_to_preorder_index[None] = self.dummy_root_preorder_index | ||
|
|
||
| def construct_left_child_tree(node): | ||
| child = None | ||
| while stack: | ||
| parent = stack.pop() | ||
| parent.right = child | ||
| if node_val_to_preorder_index[parent.val] == node_val_to_preorder_index[node.val] + 1: | ||
| return parent | ||
| child = parent | ||
|
|
||
| prev_val = None | ||
| for val in inorder: | ||
| node = TreeNode(val) | ||
| if node_val_to_preorder_index[node.val] == -1: | ||
| dummy_root = node | ||
| if prev_val is not None and node_val_to_preorder_index[node.val] < node_val_to_preorder_index[prev_val]: | ||
| node.left = construct_left_child_tree(node) | ||
| stack.append(node) | ||
| prev_val = val | ||
| return dummy_root.left | ||
| ``` | ||
|
|
||
| - odaさんのコードを見た。construct_left_treeの中でstackの後ろを見て、ダメだったら終わりにすれば、いちいちpreorder順をinorderのforループの中で調べなくていいのか | ||
|
|
||
| ```python | ||
|
|
||
| class Solution: | ||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
| node_val_to_preorder_index = {val: i for i, val in enumerate(preorder)} | ||
| stack = [] | ||
| inorder_plus_dummy = inorder + [None] | ||
| node_val_to_preorder_index[None] = -inf | ||
|
|
||
| def construct_left_tree(node): | ||
| child = None | ||
| while True: | ||
| parent = stack[-1] if stack else None | ||
|
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. ここは、 if not stack:
return child分離したほうが好みです。 この関数意味としては「preorder にしたときに node よりも後ろに来るすべての stack の中のノードを .right で繋いで返す」ですね。そうすると、条件を満たさなくなるまでフィルターする動作と linked list をつなぐ動作の合わさったコードになりそうです。
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. 「条件を満たさなくなるまでフィルターする動作」を書く時、こう書きたいなという気持ちがあるので、上の言い方になりましたが、確かに通じない言い方です。 while stack:
parent = stack[-1]
if condition:
break
move_to_next
return childでもいいかもしれません。
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. なるほど、ありがとうございます。 |
||
| if not parent or node_val_to_preorder_index[parent.val] < node_val_to_preorder_index[node.val]: | ||
| return child | ||
| parent.right = child | ||
| stack.pop() | ||
| child = parent | ||
|
|
||
| for val in inorder_plus_dummy: | ||
| node = TreeNode(val) | ||
| node.left = construct_left_tree(node) | ||
| stack.append(node) | ||
| dummy = stack[-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. None を番兵にしてもいいんですが、私は construct_left_tree の引数を node_val_to_preorder_index[node.val] にすることで -inf を入れればすべて回収されるようにしました。 |
||
| return dummy.left | ||
| ``` | ||
|
|
||
| - preorder順に見てくやつ(範囲情報も入れる)も実装してみた。 | ||
| - 最初どういうことか分かってなかったが、自分のpositionと、stackに入っている自分の先祖の中で、自分より右にある中で一番左のpositionを入れると判定できるという案か | ||
| - 後者が、Step1の自分の実装では、結果的に一個上のnodeのpositionになってる | ||
| - この考えの方が自然なのか?? | ||
| - odaさんの実装とは違い、右につながるやつの親はstackに入れっぱ | ||
|
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. 私の実装の気持ちは「stack に入っているものは、.right がまだ決まっていないもの」というつもりです。 これは preorder 順に素直に構築していっていて、そのために必要な情報が何かを考えると出てきます。
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. なるほど、「stack に入っているものは、.right がまだ決まっていないもの」「.left がまだ決まっていないものは、別に変数をおいてもいいかも」の辺で意図が結構分かってすっきりしました、ありがとうございます。 |
||
|
|
||
| ```python | ||
|
|
||
| from dataclasses import dataclass | ||
|
|
||
| @dataclass | ||
| class InorderPosition: | ||
| node: TreeNode | ||
| left_limit: int | ||
| right_limit: int | ||
|
|
||
| class Solution: | ||
| def search_parent_of_right_child(self, node: TreeNode, current_inorder_index: int, stack: List[InorderPosition]): | ||
| while stack: | ||
| if current_inorder_index < stack[-1].right_limit: | ||
| return stack[-1] | ||
| stack.pop() | ||
|
|
||
| def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: | ||
| dummy = TreeNode() | ||
| stack = [InorderPosition(dummy, inf, inf)] | ||
| node_val_to_inorder_index = {val: i for i, val in enumerate(inorder)} | ||
| for val in preorder: | ||
| node = TreeNode(val) | ||
| current_inorder_index = node_val_to_inorder_index[val] | ||
| back = stack[-1] | ||
| if current_inorder_index < back.left_limit: | ||
| back.node.left = node | ||
| stack.append(InorderPosition(node, current_inorder_index, back.left_limit)) | ||
| continue | ||
| back = self.search_parent_of_right_child(node, current_inorder_index, stack) | ||
| back.node.right = node | ||
| stack.append(InorderPosition(node, current_inorder_index, back.right_limit)) | ||
| return dummy.left | ||
| ``` | ||

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.
is_inorder という関数名から、引数が inorder というものなのかどうかを判定する関数のように感じました。良いアイデアがないのですが、もう少し分かりやすい名前を付けたほうがよいと思います。