Skip to content

617. Merge Two Binary Trees#25

Open
Ryotaro25 wants to merge 4 commits intomainfrom
problem23
Open

617. Merge Two Binary Trees#25
Ryotaro25 wants to merge 4 commits intomainfrom
problem23

Conversation

@Ryotaro25
Copy link
Copy Markdown
Owner

問題へのリンク
https://leetcode.com/problems/merge-two-binary-trees/description/

問題文(プレミアムの場合)

備考

次に解く問題の予告
Convert Sorted Array to Binary Search Tree

フォルダ構成
LeetCodeの問題ごとにフォルダを作成します。
フォルダ内は、step1.cpp、step2.cpp、step3.cpp、dfs.cpp、new_node.cppとmemo.mdとなります。

memo.md内に各ステップで感じたことを追記します。


while (!search_nodes.empty()) {
const auto [current_node, first_node, second_node] = std::move(search_nodes.front());
search_nodes.pop();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

queueに、nodeがNoneの時も突っ込んじゃって、pop()してから判定する方法もありますね(https://github.com/TORUS0818/leetcode/pull/25/files)
そうするとwhileの中の実装が簡潔になるだけでなく、22~30行目もいらなくなる気がします。

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.

@nittoco
レビューありがとうございます。
左右いずれかのnodeがnullptrであってもqueueに入れてwhileの中で処理する方式にてnew_node_step2.cppに実装しました。
こちらの方が少し簡潔に書けました。

TreeNode* new_root = new TreeNode(node1->val + node2->val);

queue<Nodes> search_nodes;
search_nodes.emplace(new_root, node1, node2);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

タプルのようなものを利用して、new_root, node1, node2を1つにまとめて突っ込んだ方がわかりやすいかなと思いました。

Copy link
Copy Markdown
Owner Author

@Ryotaro25 Ryotaro25 Aug 5, 2024

Choose a reason for hiding this comment

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

今回はstructを使って指摘の三つを一緒に管理するようにしてみました。
struct Nodes { TreeNode* current_node; TreeNode* first_node; TreeNode* second_node; };
c++ではタプルより構造体を使おうとのことでしたのでstructにしました🙇
https://google.github.io/styleguide/cppguide.html#Structs_vs._Tuples

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

なるほど、知らなかったですありがとうございます

*/
class Solution {
private:
struct Nodes {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

このstructを作ることで特にわかりやすくなっていないように感じます。

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.

@liquo-rice
ここなのですがstructを使わないでpairなどに入れる、structの中身を親の状態を保持するTreeNodeと左右を保持するpairにするなど選択肢があるのかなと思いましたがどれが分かりやすくなっているのか判断できませんでした。

元々実装でstructを使うことで親と左右のNodeを一緒に格納できて良いなと思っておりました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

メンバー変数名がイマイチなように感じました。

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.

@liquo-rice
メンバー変数名を更新しました。確かに指摘の通りfirstとsecondはよくないですね。
修正後はnode、 left_child、right_childです。nodeはparent_nodeとかにしようと思いましたが単純にnodeとしました。
cd017d4

* };
*/
class Solution {
private:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

privateはpublicのあとに書いたらいいでしょう。https://google.github.io/styleguide/cppguide.html#Declaration_Order

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.

@liquo-rice
一旦structを使ったもので、privateの位置を変えました。
定義についての順序も気をつけます。
de2aa9c

@@ -0,0 +1,44 @@
## ステップ1
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

まず再帰で書いてみるのが自然な気がします。

O(n)

新しくnodeを作る方法
TreeNodeクラスがデストラクタをどのように定義しているのか気になるところ
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

TreeNodeのdtorはtrivialだと思います。

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.

@liquo-rice
いつもありがとうございます。

A trivial destructor is a destructor that performs no action.

とございました。https://en.cppreference.com/w/cpp/language/destructor
ということは何かしら確保したリソースをdeleteする必要があるということでしょうか?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LeetCodeのTreeNodeでは、dtorが定義されていないので、defaut dtorがコンバイラによって追加されています。

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.

@liquo-rice
すみません参照した記事のすぐ下に挙動が書いてありました。失礼しました。

A trivial destructor is a destructor that performs no action. Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage. Al

search_nodes.emplace(new_root, node1, node2);

while (!search_nodes.empty()) {
const auto [current_node, first_node, second_node] = std::move(search_nodes.front());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

struct Nodesの場合、move constructorもcopy constructorも同じ処理になると思います。

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.

やはりなんとなくでしか理解していないようなので、忘れていたスクラッチでのvectorを実装してmoveの動作確認してみます。

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.

@liquo-rice
指摘されていたのは以下のようなためでしょうか。

For user-defined types, the copy behavior is defined by the copy constructor and the copy-assignment operator. Move behavior is defined by the move constructor and the move-assignment operator, if they exist, or by the copy constructor and the copy-assignment operator otherwise.

https://google.github.io/styleguide/cppguide.html#Copyable_Movable_Types
自分で定義しないとコピーが行われるということですね。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

default move ctorはメンバー変数ごとにmoveが行われます。intやポインタなどbuilt-in型の変数の場合は、単なる代入になります。

Comment on lines +41 to +42
新しく作るという手段は思いついたけど、入力と分けて管理する必要性や新しくnodeを管理するコストを考えて
片方に追加していく方式にした。
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

新しく作ることのメリット、入力を壊すデメリットについても言及しておいてもよいかもしれません

Copy link
Copy Markdown
Owner Author

@Ryotaro25 Ryotaro25 Aug 5, 2024

Choose a reason for hiding this comment

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

@fhiyo ありがとうございます。
fhiyo/leetcode#25
をもういちど拝見して下記に目を通しました🙇

入力を破壊している。
https://discord.com/channels/1084280443945353267/1252267683731345438/1252556045524537344
出力を変更されるとキャッシュが変わって次からの呼び出しが狂う。
https://discord.com/channels/1084280443945353267/1252267683731345438/1252591437485441024

TreeNode* second_node;
};

void MergeOrPushQueue(queue<TwoNodes> &search_nodes, TreeNode*& first_node, TreeNode*& second_node) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

second_nodeの型は TreeNode* const でもいい気がします。

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.

@fhiyo
コメントありがとうございます。
second_nodeだけconstをつけるとfirst_nodeにマージする際にconst有無の不一致でエラーとなりました🙇

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

それって、 const TreeNode* にしているからではないですか?

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.

@fhiyo
早速ありがとうございます。
ご指摘の通りでした。2つの書き方の違いも調べました。今回は、ポインタが指すノードを固定するようにポインタ自体を書き換えられないようにするのですね。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

@fhiyo
資料までありがとうございます。読んでみます!!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ポインターの参照は、私は軽い抵抗感がありますが、これは、Google のスタイルガイドが、大きなオブジェクトの引数は const 参照かポインターかにして、呼び出し元から書き換えられる可能性のある変数を分かるようにするというルールを敷いていたからだと思います。

あと、型の宣言の方法は一回調べておいてください。いくらでも複雑な型が作れるのでルールを理解しておいたほうがいいです。

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.

@oda

大きなオブジェクトの引数は const 参照かポインターかにして、呼び出し元から書き換えられる可能性のある変数を分かるようにするというルールを敷いていたから

この辺りでしょうか。
https://google.github.io/styleguide/cppguide.html#Use_of_const

宣言のあたりは読んだことがなかったので、型の部分に関しては以下を読んでみました。
https://google.github.io/styleguide/cppguide.html#Classes
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c-classes-and-class-hierarchies

@TORUS0818
Copy link
Copy Markdown

拝見しました。
コメントされている部分以外は良いと思います。

TreeNode* first_left = nullptr;
TreeNode* second_left = nullptr;
if (first_node) {
first_left = first_node->left;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

基本新しくノードを作っているが、ここは元々のノードを再利用しているところが、なにか一貫性のなさを感じました。

@Ryotaro25
Copy link
Copy Markdown
Owner Author

拝見しました。 コメントされている部分以外は良いと思います。

@TORUS0818
レビューありがとうございます。

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.

7 participants