-
Notifications
You must be signed in to change notification settings - Fork 0
617. Merge Two Binary Trees #23
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
Open
hiroki-horiguchi-dev
wants to merge
1
commit into
main
Choose a base branch
from
tree-bst-617
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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を使うと管理しやすいなということで、パッと思いついたが、じゃあノードの張り替えをどうするか?で完全に詰まってしまった | ||
| - 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:48Claudeが返答しました: | ||
| 確かに!いい指摘ですね。 | ||
| それって副作用の問題で、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}); // 新ノード + 両ソース | ||
| // ... | ||
|
|
||
|
|
||
| うん、何こいつ💢 | ||
| ``` | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
せっかくなので再度トライしてみて下さい。
入力の木を変更しないためには、新しい木を作ればよいですね。
例えば次のように場合分けする良いと思います。
あるノードを作るときに、
1)両方の木にノードがある場合:値を足し合わせたノードを作る
(これは、載せていただいたコードを見る限り、すでにできると思います。)
2)片方の木にしかノードがない場合:存在しない側を 0 とみなし、(存在する側の値+0)を値に持つノードを作る
3)両方とも null の場合: 何もしない
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.
ですね、再度トライします。
新しい木を作る時に、新しい木の先頭をどこに持っておくかが、パッと思いつかなくて再帰でいいやとなっています。
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.
それでしたら、以下のPRを参考にしてみてください。
https://github.com/seal-azarashi/leetcode/pull/22/changes
また、コードだけでなくPRの中のコメントについても考えてみてもいいかもしれません。