Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions tree-bst/105.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
- [105. Construct Binary Tree from Preorder and Inorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/)
- 方針
- まず大前提、preorder, inorder の性質を忘れているのでリファレンスを見にいく
- [preorder](https://www.geeksforgeeks.org/dsa/preorder-traversal-of-binary-tree/), [inorder](https://www.geeksforgeeks.org/dsa/inorder-traversal-of-binary-tree/)、問題で与えられているサンプルで考えてみる
- `Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7], Output: [3,9,20,null,null,15,7]`
- preorder の先頭が必ずrootになることはわかる
- preorder の先頭要素で inorder ないを検索すると、inorder で 3 からみた時の index 0 ~ indexOf(3), indexOf(3) + 1 ~ inorder.length - 1 がそれぞれ左部分木、右部分木だとわかる
- と言うことは、なんか preorder の値を線形に走査していく中で、うまく範囲を考えれば済むんじゃね?と考える
- 以下のように考えてみるけど、left, right 要素を作る段階でこの方針だと無理だなと考える
```java
for (int i = 0; i < preorder.length; i++) {
rootNode = new TreeNode(preorder(i));
// array とする必要はなくて、index を与えるだけで良さそう
leftNodes = new Array(inorder(0) ~ inorder.indexOf(preorder(i)));
rightNodes = new Array(inorder.indexOf(preorder(i)) + 1 ~ inorder.length - 1);

rootNode.left = // あれここからどうしよう。。再起で書かないとダメか。。

}
```
- 制約を見ると、Stack Over Flowはしなさそうなデータ量だな
```java
preorder = [3,9,20,15,7];
inorder = [9,3,15,20,7];

buildTree(int[] preorder, int[] inorder) {
// 以下の方針はダメそうだとわかる
// for (int rootVal : preOrder) {
// buildTree(rootVal, 0, inOrder - 1);
// }
this.preorder = preorder;
this.inorder = inorder;
return buildTree(0, 0, inorder.length - 1);
}

TreeNode buildTree(int preIndex, int inStart, int inEnd) {
if (inStart > inEnd) {
return null;
}
int rootVal = preOrder(preIndex);
TreeNode root = new TreeNode(rootVal);
// あれ、ここのマジックナンバーってこれでいいんだっけ?? 動的にやらなきゃいけない気がする。。
root.left = buildTree(preIndex + 1, inStart, inOrder.indexOf(rootVal));
root.right = buildTree(preIndex + 2, inOrder.indexOf(rootVal), inEnd);
return root;
}
```
- ここで時間切れ、回答みる
- やはり、preorder 側のインデックスをどう進めっるんだっけ?(マジックナンバーの箇所) が問題
- このマジックナンバーの箇所、右を構築する時は inorder でカウントできる左の要素数分を飛び越えないといけない。。これが思いつけなかった。。
- (というか、テストケースを思いつけないのが悪いな。。そもそも inorder, preorder を覚えてない時点でこの問題出されたら詰んでる。。)
- 計算量
- 実装時間

## 1st
- 方針で組み立てたものに加えていくつか工夫が必要
- 工夫で頭のメモリを取られてしまうのがまだ馴染んでいない証拠か。。
- 通るコードは書いたけど、どっちかというと inorder, preorder を正しく理解しているのか?それぞれから元の木を頭の中で組み上げられるか?の方が重要だと思う(ちなみに、inorder のみから元の構造を複合するのは無理、なぜならどこがルートかわからないから。preorder のみでも無理、[1,2] から 1 left to 2, 1 right to 2 の両方が作れてしまい、left or right が一意に定まらないから)
- 例えば、他のトラバーサルの組み合わせでは複合できるのか?とか?何と何があれば複合できるんだっけ?を理解しておけばそこまで怖くはないはず
```java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int[] preorder;
Map<Integer, Integer> inorderWithIndex = new HashMap<>();

public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for (int i = 0; i < inorder.length; i++) {
inorderWithIndex.put(inorder[i], i);
}
return buildTreeHelper(0, 0, inorder.length - 1);
}

private TreeNode buildTreeHelper(int preIndex, int inStart, int inEnd) {
if (inStart > inEnd) {
return null;
}
int rootVal = preorder[preIndex];
TreeNode root = new TreeNode(rootVal);
int rootIndexInInorder = inorderWithIndex.get(rootVal);

root.left = buildTreeHelper(preIndex + 1, inStart, rootIndexInInorder - 1);
root.right = buildTreeHelper(preIndex + (rootIndexInInorder - inStart) + 1, rootIndexInInorder + 1, inEnd);

return root;
}
}
```