Conversation
|
|
||
| while (!search_nodes.empty()) { | ||
| const auto [current_node, first_node, second_node] = std::move(search_nodes.front()); | ||
| search_nodes.pop(); |
There was a problem hiding this comment.
queueに、nodeがNoneの時も突っ込んじゃって、pop()してから判定する方法もありますね(https://github.com/TORUS0818/leetcode/pull/25/files)
そうするとwhileの中の実装が簡潔になるだけでなく、22~30行目もいらなくなる気がします。
There was a problem hiding this comment.
@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); |
There was a problem hiding this comment.
タプルのようなものを利用して、new_root, node1, node2を1つにまとめて突っ込んだ方がわかりやすいかなと思いました。
There was a problem hiding this comment.
今回はstructを使って指摘の三つを一緒に管理するようにしてみました。
struct Nodes { TreeNode* current_node; TreeNode* first_node; TreeNode* second_node; };
c++ではタプルより構造体を使おうとのことでしたのでstructにしました🙇
https://google.github.io/styleguide/cppguide.html#Structs_vs._Tuples
| */ | ||
| class Solution { | ||
| private: | ||
| struct Nodes { |
There was a problem hiding this comment.
このstructを作ることで特にわかりやすくなっていないように感じます。
There was a problem hiding this comment.
@liquo-rice
ここなのですがstructを使わないでpairなどに入れる、structの中身を親の状態を保持するTreeNodeと左右を保持するpairにするなど選択肢があるのかなと思いましたがどれが分かりやすくなっているのか判断できませんでした。
元々実装でstructを使うことで親と左右のNodeを一緒に格納できて良いなと思っておりました。
There was a problem hiding this comment.
@liquo-rice
メンバー変数名を更新しました。確かに指摘の通りfirstとsecondはよくないですね。
修正後はnode、 left_child、right_childです。nodeはparent_nodeとかにしようと思いましたが単純にnodeとしました。
cd017d4
| * }; | ||
| */ | ||
| class Solution { | ||
| private: |
There was a problem hiding this comment.
privateはpublicのあとに書いたらいいでしょう。https://google.github.io/styleguide/cppguide.html#Declaration_Order
There was a problem hiding this comment.
@liquo-rice
一旦structを使ったもので、privateの位置を変えました。
定義についての順序も気をつけます。
de2aa9c
| @@ -0,0 +1,44 @@ | |||
| ## ステップ1 | |||
| O(n) | ||
|
|
||
| 新しくnodeを作る方法 | ||
| TreeNodeクラスがデストラクタをどのように定義しているのか気になるところ |
There was a problem hiding this comment.
@liquo-rice
いつもありがとうございます。
A trivial destructor is a destructor that performs no action.
とございました。https://en.cppreference.com/w/cpp/language/destructor
ということは何かしら確保したリソースをdeleteする必要があるということでしょうか?
There was a problem hiding this comment.
LeetCodeのTreeNodeでは、dtorが定義されていないので、defaut dtorがコンバイラによって追加されています。
There was a problem hiding this comment.
@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()); |
There was a problem hiding this comment.
struct Nodesの場合、move constructorもcopy constructorも同じ処理になると思います。
There was a problem hiding this comment.
やはりなんとなくでしか理解していないようなので、忘れていたスクラッチでのvectorを実装してmoveの動作確認してみます。
There was a problem hiding this comment.
@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
自分で定義しないとコピーが行われるということですね。
There was a problem hiding this comment.
default move ctorはメンバー変数ごとにmoveが行われます。intやポインタなどbuilt-in型の変数の場合は、単なる代入になります。
| 新しく作るという手段は思いついたけど、入力と分けて管理する必要性や新しくnodeを管理するコストを考えて | ||
| 片方に追加していく方式にした。 |
There was a problem hiding this comment.
新しく作ることのメリット、入力を壊すデメリットについても言及しておいてもよいかもしれません
There was a problem hiding this comment.
@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) { |
There was a problem hiding this comment.
second_nodeの型は TreeNode* const でもいい気がします。
There was a problem hiding this comment.
@fhiyo
コメントありがとうございます。
second_nodeだけconstをつけるとfirst_nodeにマージする際にconst有無の不一致でエラーとなりました🙇
There was a problem hiding this comment.
@fhiyo
早速ありがとうございます。
ご指摘の通りでした。2つの書き方の違いも調べました。今回は、ポインタが指すノードを固定するようにポインタ自体を書き換えられないようにするのですね。
There was a problem hiding this comment.
There was a problem hiding this comment.
ポインターの参照は、私は軽い抵抗感がありますが、これは、Google のスタイルガイドが、大きなオブジェクトの引数は const 参照かポインターかにして、呼び出し元から書き換えられる可能性のある変数を分かるようにするというルールを敷いていたからだと思います。
あと、型の宣言の方法は一回調べておいてください。いくらでも複雑な型が作れるのでルールを理解しておいたほうがいいです。
There was a problem hiding this comment.
大きなオブジェクトの引数は 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
|
拝見しました。 |
| TreeNode* first_left = nullptr; | ||
| TreeNode* second_left = nullptr; | ||
| if (first_node) { | ||
| first_left = first_node->left; |
There was a problem hiding this comment.
基本新しくノードを作っているが、ここは元々のノードを再利用しているところが、なにか一貫性のなさを感じました。
@TORUS0818 |
問題へのリンク
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内に各ステップで感じたことを追記します。