Skip to content

Conversation

Space : O(V)

再帰を用いない方法で書いてみる。
参考館演算子は普段なるべく使わないが、使うとnulptr考慮がかなりスッキリかけたので利用する。
Copy link

Choose a reason for hiding this comment

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

三項演算子のことですか?「普段なるべく使わない」というのは理由があったりしますか?

Copy link
Owner Author

@colorbox colorbox Jan 27, 2025

Choose a reason for hiding this comment

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

三項演算子のことですか?

三項間演算子ですね

三項間演算子は基本的に読みづらくなるので、なるべくif ~ elseを使うようにしています。
今回はそうすると異常に冗長になったので三項間演算子を使用しました。

Copy link

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

Copy link
Owner Author

Choose a reason for hiding this comment

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

三項演算子の方が主流な呼び方なんですね

if (!root1 && !root2) {
return nullptr;
}
stack<pair<vector<TreeNode*>, bool>> next_nodes;
Copy link

Choose a reason for hiding this comment

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

next_nodesの中身がwhile文の中を読むまでわからなかったので、(C++にそういうものがあれば)構造体を定義したり、コメントをつけたりするといいと思いました。構造体を使わないとしても、vectorを使っていることに違和感がありました。何かしらの方法でleft_node, right_node, merged_nodeの3つが順に入ることを表現した方がいいと思いました。

Copy link

Choose a reason for hiding this comment

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

長さが必ず 3 の vector を使うのは、軽い抵抗があります。意味としては同じものが並んでいるわけではないので。
tuple で4つ並べるのでいいのではないかなと思います。
ただ、これくらいになってくるとそろそろ構造体を定義してもよいかもしれません。

これとだいたい同じですかね。
https://discord.com/channels/1084280443945353267/1245404801177616394/1284010446306803765

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます
最後のbool含めてそれぞれ役割が異なるので、構造体でまとめる、tupleでまとめるほうが適切ですね。

node->right = mergeTrees(root1->right, root2->right);
return node;
}
};
Copy link

@hroc135 hroc135 Jan 27, 2025

Choose a reason for hiding this comment

The 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)  # 2

Copy link

Choose a reason for hiding this comment

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

newする部分木としない部分木があるというのはこの関数を使う側としては怖いので、TreeNode* dupTree(TreeNode* root)などを別途作成して、きちんと全てのnodeをnewするのがよいと思います。

    if (!root1) return dupTree(root2);
    if (!root2) return dupTree(root1);

Copy link

Choose a reason for hiding this comment

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

空だった方を値0のノードを作って計算を続行するという方法もあるそうです
olsen-blue/Arai60#23 (comment)

Copy link

Choose a reason for hiding this comment

The 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 = {};
};

Copy link

Choose a reason for hiding this comment

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

@oda

if (!root1) {
    root1 = new TreeNode();
}

としてしまうと、tree1で使用されているメモリを解放するときに自分で作成した空のTreeNodeは解放対象にならないということでしょうか?gptに聞いても&dummyなら大丈夫な理由がいまいちよく分からなかったです

Copy link
Owner Author

Choose a reason for hiding this comment

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

newしたオブジェクトはどこかでdelete呼ばないとリークします。
ダミーとして使うroot1は、関数の外でdeleteされないのでこのままだとメモリリークしそうですね。

Copy link

Choose a reason for hiding this comment

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

なるほどです。反対にdummyはメンバ変数として定義されているのでSolutionクラスのインスタンスがdeleteされるときにdeleteされるということですか?

Copy link
Owner Author

Choose a reason for hiding this comment

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

そうですね、私はその理解です。

Copy link

Choose a reason for hiding this comment

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

理解できました。ありがとうございます

Copy link

Choose a reason for hiding this comment

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

それでよいです。ヒープメモリーの話はどこかにありましたね。
https://discord.com/channels/1084280443945353267/1237649827240742942/1251960606077091981

return nullptr;
}
stack<tuple<TreeNode*, TreeNode*, TreeNode**>> next_nodes;
auto merged_root = new TreeNode();
Copy link

Choose a reason for hiding this comment

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

ここ、nullptr でよいように思います。ループの中で中身が作られていますね。

おおむねいいと思います。
ダブルポインターによる解法はここにもあります。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.cxy3cik6kyqx

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。
確かにここの代入は不要でした。

}
stack<pair<vector<TreeNode*>, bool>> next_nodes;
auto merged_root = new TreeNode();
if (root1) {
Copy link

Choose a reason for hiding this comment

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

{0, nullptr, nullptr} な番兵を用意して、番兵のアドレスを root1 にセットすれば下の分岐がなくせますかね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

あー、なるほど、番兵を用意すると冗長な三項間演算子が消せますね、ありがとうございます。

@colorbox colorbox merged commit 025b979 into main Mar 9, 2025
@colorbox colorbox deleted the 617 branch March 9, 2025 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants