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
241 changes: 241 additions & 0 deletions tree-bst/617.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
- 問題: [617. Merge Two Binary Trees](https://leetcode.com/problems/merge-two-binary-trees/description/)
- 方針
- 再帰
- まあ書ける
- 時間計算量, 空間計算量: O(N) root1, root2 の大きい方に依存する
- スタックオーバーフローが発生する可能性を考え始めないといけない
- Queue
- root1, root2 で和をとるべき相対位置はQueueを使うと管理しやすいなということで、パッと思いついたが、じゃあノードの張り替えをどうするか?で完全に詰まってしまった

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

せっかくなので再度トライしてみて下さい。

入力の木を変更しないためには、新しい木を作ればよいですね。
例えば次のように場合分けする良いと思います。

あるノードを作るときに、
1)両方の木にノードがある場合:値を足し合わせたノードを作る
(これは、載せていただいたコードを見る限り、すでにできると思います。)
2)片方の木にしかノードがない場合:存在しない側を 0 とみなし、(存在する側の値+0)を値に持つノードを作る
3)両方とも null の場合: 何もしない

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ですね、再度トライします。
新しい木を作る時に、新しい木の先頭をどこに持っておくかが、パッと思いつかなくて再帰でいいやとなっています。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

それでしたら、以下のPRを参考にしてみてください。
https://github.com/seal-azarashi/leetcode/pull/22/changes

また、コードだけでなくPRの中のコメントについても考えてみてもいいかもしれません。

- claude が綺麗にしてくれたけど、動かない途中のコードも貼っておく
- Stack
- Stack も Queue と似たようなことになりそうなので、今回は書かない


## 再帰
```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 {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null && root2 == null) {
return null;
}

int newVal = 0;
if (root1 != null) {
newVal += root1.val;
}
if (root2 != null) {
newVal += root2.val;
}

TreeNode mergeTree = new TreeNode(newVal);
if (root1 == null) {
mergeTree.left = mergeTrees(null, root2.left);
mergeTree.right = mergeTrees(null, root2.right);
} else if (root2 == null) {
mergeTree.left = mergeTrees(root1.left, null);
mergeTree.right = mergeTrees(root1.right, null);
} else {
mergeTree.left = mergeTrees(root1.left, root2.left);
mergeTree.right = mergeTrees(root1.right, root2.right);
}

return mergeTree;
}
}
```

- 条件分岐が冗長なので削る、最後の else ブロックが残せるようにするにはどうしたらいいかを考えると、、

```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 {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
}
if (root2 == null) {
return root1;
}

TreeNode mergeTree = new TreeNode(root1.val + root2.val);
mergeTree.left = mergeTrees(root1.left, root2.left);
mergeTree.right = mergeTrees(root1.right, root2.right);

return mergeTree;
}
}
```

## Queue
```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 {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
}
if (root2 == null) {
return root1;
}

Queue<TreeNode> nodes1 = new ArrayDeque<>();
nodes1.add(root1);
Queue<TreeNode> nodes2 = new ArrayDeque<>();
nodes2.add(node2);
Queue<TreeNode> newNode = new ArrayDeque<>();

while (!nodes1.isEmpty() || !nodes2.isEmpty()) {
TreeNode node1 = nodes1.poll();
TreeNode node2 = nodes2.poll();

int newVal = 0;
if (node1 != null) {
newVal += node1.val;
nodes1.add(node1.left);
nodes1.add(node1.right);
}
if (node2 != null) {
newVal += node2.val;
nodes2.add(node2.left);
nodes2.add(node2.right);
}

TreeNode newNode = new TreeNode(newVal);
newNode.add(newNode);

while (!newNode.isEmpty()) {
TreeNode node = newNode.poll();
}
}
}
}
```
- claude に修正させたもの
- これは root1 を破壊的に変更するものなので私は良くないと思う
```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 {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) return root2;
if (root2 == null) return root1;
// root1のノードとroot2のノードを常にペアで管理する
Queue<TreeNode[]> queue = new ArrayDeque<>();
queue.add(new TreeNode[]{root1, root2});

while (!queue.isEmpty()) {
TreeNode[] pair = queue.poll();
TreeNode n1 = pair[0];
TreeNode n2 = pair[1];

// n1にn2をマージしていく
n1.val += n2.val;

if (n1.left == null) {
n1.left = n2.left; // そのままつける
} else if (n2.left != null) {
queue.add(new TreeNode[]{n1.left, n2.left}); // 両方あるなら後で処理
}

if (n1.right == null) {
n1.right = n2.right;
} else if (n2.right != null) {
queue.add(new TreeNode[]{n1.right, n2.right});
}
}
return root1;
}
}
```
```text
いいと思うんだけどこの関数を使っているユーザーは驚くと思わない?
関数に与えたら root1 が完全に書き換わって帰ってくるんだよ?

1:48Claudeが返答しました:
確かに!いい指摘ですね。
それって副作用の問題で、mergeTrees(root1, root2)を呼んだら元のroot1が破壊されるのは呼び出し側からすると予想外ですよね。
LeetCodeだから許容されてるけど、プロダクトコードだったら完全にアウトで、新しいノードを作って返すのが正しい設計です。
java// 呼び出し側の気持ち
TreeNode merged = mergeTrees(root1, root2);
// root1がまだ元のままだと思ってたのに書き換わってる!😱
なので再帰の方が:

副作用なし
コードもシンプル

で、素直に優れてますね。BFSで練習するのはいいけど、実務・設計観点では再帰の方が正直おすすめです👍
```

- 本当か?と思って上記を別チャットでレビューさせると
```text
会話の内容について
副作用の指摘は正しいです。ただ、Claudeの返答にやや雑な点があります。

「BFSで練習するのはいいけど、実務・設計観点では再帰の方が正直おすすめです」

これは少し乱暴で、再帰はスタックオーバーフローのリスクがあるという重要な観点が抜けています。木が深い(数万ノード)場合、BFSの方が安全なこともあります。副作用なしのBFS解も書けます。
java// 副作用なし + BFS(新規ノードを作る)
Queue<TreeNode[]> queue = new ArrayDeque<>();
TreeNode root = new TreeNode(root1.val + root2.val);
queue.add(new TreeNode[]{root, root1, root2}); // 新ノード + 両ソース
// ...


うん、何こいつ💢
```