-
Notifications
You must be signed in to change notification settings - Fork 0
105. Construct Binary Tree from Preorder and Inorder Traversal #43
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,38 @@ | ||
/* | ||
Solve Time : 21:05 | ||
|
||
Time: O(N^2) | ||
Space: O(N) | ||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { | ||
if (preorder.size() == 0) { | ||
return nullptr; | ||
} | ||
// preorder_first element is root | ||
auto root = new TreeNode(preorder[0]); | ||
if (preorder.size() == 1) { | ||
return root; | ||
} | ||
// get index of root in inorder | ||
int inorder_root_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.
|
||
for (int i = 0; i < inorder.size(); ++i) { | ||
if (inorder[i] == preorder[0]) { | ||
inorder_root_index = i; | ||
break; | ||
} | ||
} | ||
// get left right tree node count | ||
int left_nodes_count = inorder_root_index; | ||
// split vectors and recursive to left right | ||
vector<int> preorder_left(preorder.begin() + 1, preorder.begin() + 1 + left_nodes_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. inorderのルートを起点に左右に分割しvectorに要素を詰め込んでいると思うのですが |
||
vector<int> inorder_left(inorder.begin(), inorder.begin() + left_nodes_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. C++20 からで私はあまり知らないですが、subrange 使う手はありますね。 TreeNode* buildTree(std::vector<int>& preorder, std::vector<int>& inorder) {
return buildTreeHelper(
std::ranges::subrange(preorder.begin(), preorder.end()),
std::ranges::subrange(inorder.begin(), inorder.end())
);
}
TreeNode* buildTreeHelper(std::ranges::subrange preorder, std::ranges::subrange inorder) { |
||
root->left = buildTree(preorder_left, inorder_left); | ||
|
||
vector<int> preorder_right(preorder.begin() + left_nodes_count + 1, preorder.end()); | ||
vector<int> inorder_right(inorder.begin() + left_nodes_count + 1, inorder.end()); | ||
root->right = buildTree(preorder_right, inorder_right); | ||
return root; | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
Time: O(N) | ||
Space: O(N) | ||
https://github.com/kazukiii/leetcode/pull/30#discussion_r1821506570 | ||
のコードが直感的に理解しづらかったので理解のために書いた | ||
大筋の処理としては、preorderの順番に処理、inorderの位置情報を利用。 | ||
inorderの、特定のノードを境に左部分木と右部分木に分かれる性質を活用する。 | ||
|
||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { | ||
map<int, int> value_to_inorder_position; | ||
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. 個人的には value_to_inorder_index のほうが分かりやすいように思いました。 |
||
for (int i = 0; i < inorder.size(); i++) { | ||
value_to_inorder_position[inorder[i]] = i; | ||
} | ||
TreeNode dummy = TreeNode(); | ||
stack<tuple<TreeNode*, int, int>> nodes_and_limits; | ||
// 番兵の設置、left/rightのlimitはinorderにおける左右部分木の存在可能な限界を示す、番兵の初期値としてmaxを与える | ||
nodes_and_limits.push({&dummy, numeric_limits<int>::max(), numeric_limits<int>::max()}); | ||
for (int p: preorder) { | ||
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. p のあとにスペースを空けることをお勧めします。 |
||
auto node = new TreeNode(p); | ||
auto node_inorder_position = value_to_inorder_position[p]; | ||
auto [parent_node, parent_left_limit, parent_right_limit] = nodes_and_limits.top(); | ||
// inorderの位置が、暫定親親の持つ左部分木の限界よりも小さい場合、暫定親は親に確定、その左部分木にnodeを追加する | ||
// preorderを順番に処理しているので、inorderにおける下記位置条件を満たすなら、常に直前の要素の左部分木のrootとなる | ||
if (node_inorder_position < parent_left_limit) { | ||
parent_node->left = node; | ||
nodes_and_limits.push({node, node_inorder_position, parent_left_limit}); | ||
continue; | ||
} | ||
// 左部分木に関する条件を満たさないので、右部分木の始点を探す | ||
while (!nodes_and_limits.empty()) { | ||
// 現在処理中のnodeのinorderにおける位置が、暫定親の右部分木の限界よりも小さい場合、その暫定親の右部分木のrootとして追加する | ||
auto [parent_node, parent_left_limit, parent_right_limit] = nodes_and_limits.top(); | ||
// 暫定親(stack末尾ノード)の右部分木の上限よりも小さいなら、そこが右部分木の始点となる | ||
// ここに到達するまでに処理した暫定親は右部分木の限界がnodeの位置よりも左にあるため、右部分木の始点となれない | ||
if (node_inorder_position < parent_right_limit) { | ||
// 右部分木の親が確定したら右部分木を生やして新たな処理対応としてstackへpush | ||
parent_node->right = node; | ||
nodes_and_limits.pop(); | ||
nodes_and_limits.push({node, node_inorder_position, parent_right_limit}); | ||
break; | ||
} | ||
// 次の暫定親の確認のためpop | ||
nodes_and_limits.pop(); | ||
} | ||
} | ||
return dummy.left; | ||
} | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
2_1_1を理解するために番兵を除去したコード | ||
left_limitは左部分木の右側の限界位置、right_limitが右部分木の右側の限界位置をそれぞれ示す | ||
それがstackに積んだrootを含むtupleから直感的に理解できる | ||
forの中にwhileと捉えるとわかりづらくなるが、シンプルにpreorder traversalと捉えるとわかりやすくなる | ||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { | ||
map<int, int> value_to_inorder_position; | ||
for (int i = 0; i < inorder.size(); i++) { | ||
value_to_inorder_position[inorder[i]] = i; | ||
} | ||
auto root = new TreeNode(preorder[0]); | ||
stack<tuple<TreeNode*, int, int>> nodes_and_limits; | ||
nodes_and_limits.push({root, value_to_inorder_position[root->val], preorder.size()}); | ||
for (int i = 1; i < preorder.size(); ++i) { | ||
auto p = preorder[i]; | ||
auto node = new TreeNode(p); | ||
auto node_inorder_index = value_to_inorder_position[node->val]; | ||
auto [parent_node, parent_left_limit, parent_right_limit] = nodes_and_limits.top(); | ||
if (node_inorder_index < parent_left_limit) { | ||
parent_node->left = node; | ||
nodes_and_limits.push({node, node_inorder_index, parent_left_limit}); | ||
continue; | ||
} | ||
while (!nodes_and_limits.empty()) { | ||
auto [parent_node, parent_left_limit, parent_right_limit] = nodes_and_limits.top(); | ||
if (node_inorder_index < parent_right_limit) { | ||
nodes_and_limits.pop(); | ||
parent_node->right = node; | ||
// 左部分木のrootをstackにpushする時に、right_limitが親のleft_limitになるのは、親の位置が右部分木の限界位置だから | ||
// 右部分木のrootをstackにpushする時、親のright_limitがそのまま使われるのは、親の位置によって右部分木の限界位置が定まらないから | ||
nodes_and_limits.push({node, node_inorder_index, parent_right_limit}); | ||
break; | ||
} | ||
nodes_and_limits.pop(); | ||
} | ||
} | ||
|
||
return root; | ||
} | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
https://github.com/kazukiii/leetcode/pull/30#discussion_r1821628634 | ||
を理解するための練習 | ||
inorder traversalの動きに合わせて構築も同時にしていると捉えるとわかりやすかった | ||
大筋としては、inorder順の処理、preorderの位置情報、の二点を活用することで構築を行っている。 | ||
inorderの順番で処理しているので、特定のノードの処理が完了すると、そのノードをrootとする部分木の構築が完了している。 | ||
|
||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { | ||
map<int, int> value_to_preorder_position; | ||
for (int i = 0; i < preorder.size(); ++i) { | ||
value_to_preorder_position[preorder[i]] = i; | ||
} | ||
stack<TreeNode*> nodes; | ||
// | ||
auto gather_descendants = [&](int node_position) { | ||
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. node_position が preorder の index か inorder の index か分かりづらく感じました。 preorder_index はいかがでしょうか? |
||
TreeNode* child = nullptr; | ||
while (!nodes.empty()) { | ||
auto back = nodes.top(); | ||
// stackの | ||
if (value_to_preorder_position[back->val] < node_position) { | ||
break; | ||
} | ||
nodes.pop(); | ||
back->right = child; | ||
child = back; | ||
} | ||
return child; | ||
}; | ||
for (int i: inorder) { | ||
auto node = new TreeNode(i); | ||
int node_position = value_to_preorder_position[node->val]; | ||
// inorder順に処理している | ||
node->left = gather_descendants(node_position); | ||
nodes.push(node); | ||
} | ||
return gather_descendants(-1); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
step1をインデックスを使用して高速化するパターン | ||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* buildTree(vector<int>& preorder, vector<int>& 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. buildTreeWithIndex() を呼び出しているだけのため、空行は消してしまってよいと思います。 |
||
|
||
return buildTreeWithIndex(0, 0, preorder.size(), preorder, inorder); | ||
} | ||
|
||
TreeNode* buildTreeWithIndex(int preorder_root_index, int inorder_left, int inorder_right, vector<int>& preorder, vector<int>& 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. これはpublicでなくてもいいかなと思いました。 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. preorderとinorderにどちらもconstをつけると、関数内では変更しないことをこの行で伝えられると思いました。 |
||
if (inorder_left >= inorder_right) { | ||
return nullptr; | ||
} | ||
auto root = new TreeNode(preorder[preorder_root_index]); | ||
|
||
int root_index; | ||
for (int i = inorder_left; i < inorder_right; ++i) { | ||
if (inorder[i] == root->val) { | ||
root_index = i; | ||
break; | ||
} | ||
} | ||
root->left = buildTreeWithIndex(preorder_root_index + 1, inorder_left, root_index, preorder, inorder); | ||
root->right = buildTreeWithIndex(preorder_root_index + 1 + (root_index - inorder_left), root_index + 1, inorder_right, 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. 一つ目の引数ですが、ChatGptを使って理解できた(?)レベルでした。 |
||
return root; | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
class Solution { | ||
public: | ||
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { | ||
if (preorder.size() == 0) { | ||
return nullptr; | ||
} | ||
auto root = new TreeNode(preorder[0]); | ||
int inorder_root_index; | ||
for (int i = 0; i < inorder.size(); ++i) { | ||
if (inorder[i] == root->val) { | ||
inorder_root_index = i; | ||
break; | ||
} | ||
} | ||
|
||
vector<int> left_preorder(preorder.begin() + 1, preorder.begin() + 1 + inorder_root_index); | ||
vector<int> left_inorder(inorder.begin(), inorder.begin() + inorder_root_index); | ||
root->left = buildTree(left_preorder, left_inorder); | ||
|
||
vector<int> right_preorder(preorder.begin() + 1 + inorder_root_index, preorder.end()); | ||
vector<int> right_inorder(inorder.begin() + 1 + inorder_root_index, inorder.end()); | ||
root->right = buildTree(right_preorder, right_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. preorder.begin() + 1 + inorder_root_index や inorder.begin() + inorder_root_index を変数としておくと区間をある境で分けていることが明確になる気もしました。+1 は末尾に書きたいと思いましたが趣味の範囲かもしれません。 |
||
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.
if (preorder.empty()) {
のほうがシンプルだと思います。