-
Notifications
You must be signed in to change notification settings - Fork 0
617. Merge Two Binary Trees #37
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
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,28 @@ | ||
| /* | ||
| Solve Time : 09:16 | ||
|
|
||
| Time : O(V) | ||
| Space : O(V) | ||
|
|
||
| rootの宣言周りのミスで無駄に手間取ってしまった。 | ||
| newをつかうべきだったがそれを失念していた。 | ||
| それ以外は特に問題なくクリア、気分的にとりあえず再帰で解いた。 | ||
| */ | ||
| class Solution { | ||
| public: | ||
| TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { | ||
| if (!root1 && !root2) { | ||
| return nullptr; | ||
| } | ||
| if (!root1) { | ||
| return root2; | ||
| } | ||
| if (!root2) { | ||
| return root1; | ||
| } | ||
| TreeNode *root = new TreeNode(root1->val + root2->val); | ||
| root->left = mergeTrees(root1->left, root2->left); | ||
| root->right = mergeTrees(root1->right, root2->right); | ||
| return root; | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /* | ||
| Time : O(V) | ||
| Space : O(V) | ||
|
|
||
| step2_1の改良前 | ||
| */ | ||
|
|
||
| class Solution { | ||
| public: | ||
| TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { | ||
| if (!root1 && !root2) { | ||
| return nullptr; | ||
| } | ||
| stack<pair<vector<TreeNode*>, bool>> next_nodes; | ||
| auto merged_root = new TreeNode(); | ||
| if (root1) { | ||
| merged_root->val += root1->val; | ||
| } | ||
| if (root2) { | ||
| merged_root->val += root2->val; | ||
| } | ||
| next_nodes.push({{(root1 ? root1->left : nullptr), (root2 ? root2->left : nullptr), merged_root}, true}); | ||
| next_nodes.push({{(root1 ? root1->right : nullptr), (root2 ? root2->right : nullptr), merged_root}, false}); | ||
| while (!next_nodes.empty()) { | ||
| auto [nodes, is_left] = next_nodes.top(); | ||
| next_nodes.pop(); | ||
| auto node1 = nodes[0]; | ||
| auto node2 = nodes[1]; | ||
| auto parent_node = nodes[2]; | ||
| if (!node1 && !node2) { | ||
| continue; | ||
| } | ||
| int merged_val = (node1 ? node1->val : 0) + (node2 ? node2->val : 0); | ||
| auto merged_node = new TreeNode(merged_val); | ||
| if (is_left) { | ||
| parent_node->left = merged_node; | ||
| } else { | ||
| parent_node->right = merged_node; | ||
| } | ||
| if (!node2) { | ||
| next_nodes.push({{node1->left, nullptr, merged_node}, true}); | ||
| next_nodes.push({{node1->right, nullptr, merged_node}, false}); | ||
| continue; | ||
| } | ||
| if (!node1) { | ||
| next_nodes.push({{nullptr, node2->left, merged_node}, true}); | ||
| next_nodes.push({{nullptr, node2->right, merged_node}, false}); | ||
| continue; | ||
| } | ||
| next_nodes.push({{node1->left, node2->left, merged_node}, true}); | ||
| next_nodes.push({{node1->right, node2->right, merged_node}, false}); | ||
| } | ||
| return merged_root; | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| /* | ||
| Time : O(V) | ||
| Space : O(V) | ||
|
|
||
| 再帰を用いない方法で書いてみる。 | ||
| 参考館演算子は普段なるべく使わないが、使うとnulptr考慮がかなりスッキリかけたので利用する。 | ||
| */ | ||
| class Solution { | ||
| public: | ||
| TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { | ||
| if (!root1 && !root2) { | ||
| return nullptr; | ||
| } | ||
| stack<pair<vector<TreeNode*>, bool>> next_nodes; | ||
|
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. next_nodesの中身がwhile文の中を読むまでわからなかったので、(C++にそういうものがあれば)構造体を定義したり、コメントをつけたりするといいと思いました。構造体を使わないとしても、vectorを使っていることに違和感がありました。何かしらの方法でleft_node, right_node, merged_nodeの3つが順に入ることを表現した方がいいと思いました。 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. 長さが必ず 3 の vector を使うのは、軽い抵抗があります。意味としては同じものが並んでいるわけではないので。 これとだいたい同じですかね。
Owner
Author
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. ありがとうございます |
||
| auto merged_root = new TreeNode(); | ||
| if (root1) { | ||
|
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. {0, nullptr, nullptr} な番兵を用意して、番兵のアドレスを root1 にセットすれば下の分岐がなくせますかね。
Owner
Author
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. あー、なるほど、番兵を用意すると冗長な三項間演算子が消せますね、ありがとうございます。 |
||
| merged_root->val += root1->val; | ||
| } | ||
| if (root2) { | ||
| merged_root->val += root2->val; | ||
| } | ||
| next_nodes.push({{(root1 ? root1->left : nullptr), (root2 ? root2->left : nullptr), merged_root}, true}); | ||
| next_nodes.push({{(root1 ? root1->right : nullptr), (root2 ? root2->right : nullptr), merged_root}, false}); | ||
| while (!next_nodes.empty()) { | ||
| auto [nodes, is_left] = next_nodes.top(); | ||
| next_nodes.pop(); | ||
| auto node1 = nodes[0]; | ||
| auto node2 = nodes[1]; | ||
| auto parent_node = nodes[2]; | ||
| if (!node1 && !node2) { | ||
| continue; | ||
| } | ||
| int merged_val = (node1 ? node1->val : 0) + (node2 ? node2->val : 0); | ||
| auto merged_node = new TreeNode(merged_val); | ||
| if (is_left) { | ||
| parent_node->left = merged_node; | ||
| } else { | ||
| parent_node->right = merged_node; | ||
| } | ||
| next_nodes.push({{(node1 ? node1->left : nullptr), (node2 ? node2->left : nullptr), merged_node}, true}); | ||
| next_nodes.push({{(node1 ? node1->right : nullptr), (node2 ? node2->right : nullptr), merged_node}, false}); | ||
| } | ||
| return merged_root; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /* | ||
| Time : O(V) | ||
| Space : O(V) | ||
|
|
||
| ポインタを用いて簡略化したもの | ||
| */ | ||
| class Solution { | ||
| public: | ||
| TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { | ||
| if (!root1 && !root2) { | ||
| return nullptr; | ||
| } | ||
| stack<tuple<TreeNode*, TreeNode*, TreeNode**>> next_nodes; | ||
| auto merged_root = new TreeNode(); | ||
|
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. ここ、nullptr でよいように思います。ループの中で中身が作られていますね。 おおむねいいと思います。
Owner
Author
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. ありがとうございます。 |
||
| next_nodes.push({root1, root2, &merged_root}); | ||
| while (!next_nodes.empty()) { | ||
| auto [node1, node2, merged_node_ptr] = next_nodes.top(); | ||
| next_nodes.pop(); | ||
| if (!node1 && !node2) { | ||
| continue; | ||
| } | ||
| int merged_val = (node1 ? node1->val : 0) + (node2 ? node2->val : 0); | ||
| TreeNode* merged_node = new TreeNode(merged_val); | ||
| *merged_node_ptr = merged_node; | ||
| next_nodes.push({(node1 ? node1->left : nullptr), (node2 ? node2->left : nullptr), &merged_node->left}); | ||
| next_nodes.push({(node1 ? node1->right : nullptr), (node2 ? node2->right : nullptr), &merged_node->right}); | ||
| } | ||
| return merged_root; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| class Solution { | ||
| public: | ||
| TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { | ||
| if (!root1 && !root2) { | ||
| return nullptr; | ||
| } | ||
| stack<pair<vector<TreeNode*>, bool>> next_nodes; | ||
| auto merged_root = new TreeNode(); | ||
| if (!root1) { | ||
| root1 = new TreeNode(); | ||
| } | ||
| if (!root2) { | ||
| root2 = new TreeNode(); | ||
| } | ||
| merged_root->val += root1->val + root2->val; | ||
| next_nodes.push({{root1->left, root2->left, merged_root}, true}); | ||
| next_nodes.push({{root1->right, root2->right, merged_root}, false}); | ||
| while (!next_nodes.empty()) { | ||
| auto [nodes, is_left] = next_nodes.top(); | ||
| next_nodes.pop(); | ||
| auto node1 = nodes[0]; | ||
| auto node2 = nodes[1]; | ||
| auto parent_node = nodes[2]; | ||
| if (!node1 && !node2) { | ||
| continue; | ||
| } | ||
| int merged_val = (node1 ? node1->val : 0) + (node2 ? node2->val : 0); | ||
| auto merged_node = new TreeNode(merged_val); | ||
| if (is_left) { | ||
| parent_node->left = merged_node; | ||
| } else { | ||
| parent_node->right = merged_node; | ||
| } | ||
| next_nodes.push({{(node1 ? node1->left : nullptr), (node2 ? node2->left : nullptr), merged_node}, true}); | ||
| next_nodes.push({{(node1 ? node1->right : nullptr), (node2 ? node2->right : nullptr), merged_node}, false}); | ||
| } | ||
| return merged_root; | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| class Solution { | ||
| public: | ||
| TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { | ||
| if (!root1 && !root2) { | ||
| return nullptr; | ||
| } | ||
| stack<tuple<TreeNode*, TreeNode*, TreeNode**>> next_nodes; | ||
| TreeNode* merged_root; | ||
| next_nodes.push({root1, root2, &merged_root}); | ||
| while (!next_nodes.empty()) { | ||
| auto [node1, node2, merged_node_ptr] = next_nodes.top(); | ||
| next_nodes.pop(); | ||
| if (!node1 && !node2) { | ||
| continue; | ||
| } | ||
| int merged_val = (node1 ? node1->val : 0) + (node2 ? node2->val : 0); | ||
| TreeNode* merged_node = new TreeNode(merged_val); | ||
| *merged_node_ptr = merged_node; | ||
| next_nodes.push({(node1 ? node1->left : nullptr), (node2 ? node2->left : nullptr), &merged_node->left}); | ||
| next_nodes.push({(node1 ? node1->right : nullptr), (node2 ? node2->right : nullptr), &merged_node->right}); | ||
| } | ||
| return merged_root; | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| class Solution { | ||
| public: | ||
| TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { | ||
| if (!root1) { | ||
| return root2; | ||
| } | ||
| if (!root2) { | ||
| return root1; | ||
| } | ||
| TreeNode* node = new TreeNode(root1->val + root2->val); | ||
| node->left = mergeTrees(root1->left, root2->left); | ||
| node->right = mergeTrees(root1->right, root2->right); | ||
| return node; | ||
| } | ||
| }; | ||
|
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. このコードだと入力の破壊はしていませんが、入力の木と出力の木でノードが共有されている場合があることに注意しないといけないと思います。例えば下のような場合(pythonコード)にroot1しか書き換えていないつもりがmerged_treeまで書き換わっているということがあります。2_1と2_2のコードだとその点大丈夫そうです root1 = TreeNode(1)
root2 = None
merged_tree = mergeTrees(root1, root2)
print(merged_tree.val) # 1
root1.val += 1
print(merged_tree.val) # 2There 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. newする部分木としない部分木があるというのはこの関数を使う側としては怖いので、 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. 空だった方を値0のノードを作って計算を続行するという方法もあるそうです 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++ だとメモリーリークの関係から一工夫いりますね。 class Solution {
public:
TreeNode* mergeTrees(const TreeNode* root1, const TreeNode* root2) const {
if (!root1 && !root2) {
return nullptr;
}
if (!root1) {
root1 = &dummy;
}
if (!root2) {
root2 = &dummy;
}
TreeNode* node = new TreeNode(root1->val + root2->val);
node->left = mergeTrees(root1->left, root2->left);
node->right = mergeTrees(root1->right, root2->right);
return node;
}
private:
const TreeNode dummy = {};
};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. if (!root1) {
root1 = new TreeNode();
}としてしまうと、tree1で使用されているメモリを解放するときに自分で作成した空のTreeNodeは解放対象にならないということでしょうか?gptに聞いても&dummyなら大丈夫な理由がいまいちよく分からなかったです
Owner
Author
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. newしたオブジェクトはどこかでdelete呼ばないとリークします。 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. なるほどです。反対にdummyはメンバ変数として定義されているのでSolutionクラスのインスタンスがdeleteされるときにdeleteされるということですか?
Owner
Author
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. そうですね、私はその理解です。 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. 理解できました。ありがとうございます 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. それでよいです。ヒープメモリーの話はどこかにありましたね。 |
||
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.
三項演算子のことですか?「普段なるべく使わない」というのは理由があったりしますか?
Uh oh!
There was an error while loading. Please reload this page.
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 ~ elseを使うようにしています。
今回はそうすると異常に冗長になったので三項間演算子を使用しました。
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.
C++特有の事情があるのかなと思って聞いてみましたが、そういうわけではなかったのですね。ありがとうございます
「間」はいらないと思います
https://ja.wikipedia.org/wiki/%E4%B8%89%E9%A0%85%E6%BC%94%E7%AE%97%E5%AD%90
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.
三項演算子の方が主流な呼び方なんですね