diff --git a/105/step1.cpp b/105/step1.cpp new file mode 100644 index 0000000..5abef44 --- /dev/null +++ b/105/step1.cpp @@ -0,0 +1,38 @@ +/* +Solve Time : 21:05 + +Time: O(N^2) +Space: O(N) +*/ +class Solution { + public: + TreeNode* buildTree(vector& preorder, vector& 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; + 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 preorder_left(preorder.begin() + 1, preorder.begin() + 1 + left_nodes_count); + vector inorder_left(inorder.begin(), inorder.begin() + left_nodes_count); + root->left = buildTree(preorder_left, inorder_left); + + vector preorder_right(preorder.begin() + left_nodes_count + 1, preorder.end()); + vector inorder_right(inorder.begin() + left_nodes_count + 1, inorder.end()); + root->right = buildTree(preorder_right, inorder_right); + return root; + } + }; diff --git a/105/step2_1_1.cpp b/105/step2_1_1.cpp new file mode 100644 index 0000000..bab2588 --- /dev/null +++ b/105/step2_1_1.cpp @@ -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& preorder, vector& inorder) { + map value_to_inorder_position; + for (int i = 0; i < inorder.size(); i++) { + value_to_inorder_position[inorder[i]] = i; + } + TreeNode dummy = TreeNode(); + stack> nodes_and_limits; + // 番兵の設置、left/rightのlimitはinorderにおける左右部分木の存在可能な限界を示す、番兵の初期値としてmaxを与える + nodes_and_limits.push({&dummy, numeric_limits::max(), numeric_limits::max()}); + for (int p: preorder) { + 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; + } + }; + \ No newline at end of file diff --git a/105/step2_1_2.cpp b/105/step2_1_2.cpp new file mode 100644 index 0000000..b5b28b7 --- /dev/null +++ b/105/step2_1_2.cpp @@ -0,0 +1,44 @@ +/* +2_1_1を理解するために番兵を除去したコード +left_limitは左部分木の右側の限界位置、right_limitが右部分木の右側の限界位置をそれぞれ示す +それがstackに積んだrootを含むtupleから直感的に理解できる +forの中にwhileと捉えるとわかりづらくなるが、シンプルにpreorder traversalと捉えるとわかりやすくなる +*/ +class Solution { + public: + TreeNode* buildTree(vector& preorder, vector& inorder) { + map 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> 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; + } + }; + \ No newline at end of file diff --git a/105/step2_2.cpp b/105/step2_2.cpp new file mode 100644 index 0000000..ba08748 --- /dev/null +++ b/105/step2_2.cpp @@ -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& preorder, vector& inorder) { + map value_to_preorder_position; + for (int i = 0; i < preorder.size(); ++i) { + value_to_preorder_position[preorder[i]] = i; + } + stack nodes; + // + auto gather_descendants = [&](int node_position) { + 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); + } +}; diff --git a/105/step2_3.cpp b/105/step2_3.cpp new file mode 100644 index 0000000..067511d --- /dev/null +++ b/105/step2_3.cpp @@ -0,0 +1,29 @@ +/* +step1をインデックスを使用して高速化するパターン +*/ +class Solution { +public: + TreeNode* buildTree(vector& preorder, vector& inorder) { + + + return buildTreeWithIndex(0, 0, preorder.size(), preorder, inorder); + } + + TreeNode* buildTreeWithIndex(int preorder_root_index, int inorder_left, int inorder_right, vector& preorder, vector& inorder) { + 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); + return root; + } +}; diff --git a/105/step3.cpp b/105/step3.cpp new file mode 100644 index 0000000..fd770b5 --- /dev/null +++ b/105/step3.cpp @@ -0,0 +1,26 @@ +class Solution { +public: + TreeNode* buildTree(vector& preorder, vector& 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 left_preorder(preorder.begin() + 1, preorder.begin() + 1 + inorder_root_index); + vector left_inorder(inorder.begin(), inorder.begin() + inorder_root_index); + root->left = buildTree(left_preorder, left_inorder); + + vector right_preorder(preorder.begin() + 1 + inorder_root_index, preorder.end()); + vector right_inorder(inorder.begin() + 1 + inorder_root_index, inorder.end()); + root->right = buildTree(right_preorder, right_inorder); + + return root; + } +};