Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions 102.BinaryTreeLevelOrderTraversal/dfs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if (!root) {
return {};
}
Comment on lines +15 to +17
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
ご指摘のとおり不要でした。修正しました。


vector<vector<int>> level_to_values;
ConstructLevelOrder(root, level_to_values, 0);
return level_to_values;
}

private:
void ConstructLevelOrder(const TreeNode* node , vector<vector<int>>& level_to_values, int level) {
if (!node) {
return;
}
if (level >= level_to_values.size()) {
level_to_values.push_back({});
}
level_to_values[level].push_back(node->val);
ConstructLevelOrder(node->left, level_to_values, level + 1);
ConstructLevelOrder(node->right, level_to_values, level + 1);
}
};
32 changes: 32 additions & 0 deletions 102.BinaryTreeLevelOrderTraversal/dfs_step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> level_to_values;
ConstructLevelOrder(root, level_to_values, 0);
return level_to_values;
}

private:
void ConstructLevelOrder(const TreeNode* node , vector<vector<int>>& level_to_values, int level) {
if (!node) {
return;
}
if (level >= level_to_values.size()) {
level_to_values.push_back({});
}
level_to_values[level].push_back(node->val);
ConstructLevelOrder(node->left, level_to_values, level + 1);
ConstructLevelOrder(node->right, level_to_values, level + 1);
}
};
79 changes: 79 additions & 0 deletions 102.BinaryTreeLevelOrderTraversal/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
## ステップ1
深さと値をセットで保存しておけば解けそう

level_to_values[depth].emplace_back(node->val);の部分を
返却用のvector<vector<int>>で行いたかったが
vectorに対してサイズ確定前のインデックスにアクセスするとエラーとなる
なのでmapを挟む形にした。15分ほど

時間計算量
O(n log n)
空間計算量
O(n)

## ステップ2
・queueに入れてから、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.

あくまで個人的ですが、自分はqueueに入れる前に判断する方が好きです。
ゴミかもしれないけどとりあえずqueueに入れて後で調整する、という方法だとちょっと認知負荷がある気がしたので...今回だと if (!values.empty()) { ... } の条件文を自分だと忘れそうだなと思いました

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.

確かにこれまでの類似問題と比較しても、if (!values.empty()) { ... }は特別に処理を入れてますね。
yoshikiさんからのレビュー内容も踏まえて、queue管理をvectorに変更しvectorに入れる前に判断する方式に変更しました。ありがとうございます。

短くかける
下記を参考に変更
https://github.com/shining-ai/leetcode/pull/26/commits/c90e9ee927b79c6002701924828b3eefcb3281bc

・データの持ち方変更
whileループの中でさらにループを作りvectorを作りその中に同じ階層のvalueを突っ込んで
内側のループ後にvector<vector<int>>に突っ込めばmapに変形する必要がなくなる
登場人物が減るので読み易い
また、階層情報と値を対でもつつようがなくなる

queueのサイズをforループの宣言内で定義していてハマった。
ループ内でqueueに突っ込んでいくので想定外の動きとなる

・変数名変更
vector<vector<int>>を格納する変数をvaluesからlevel_to_values
値(たち)に対する階層

## 他の解法
dfs(再帰)でも解いてみる
下記を参考
https://leetcode.com/problems/binary-tree-level-order-traversal/solutions/3196962/c-bfs-dfs-o-n-explained/

関数化しないで描こうとしたが、BFSとあまり変わらない感じになったのでやめた
queueを使った方が再帰より処理が追い易いと感じる
時間計算量
O(n)
空間計算量
O(n)

## 先延ばしにしていたstd::moveとcopy周りについて調べる
・vectorをスクラッチで定義
49. Group Anagramsでアドバイス頂いている
>練習として、簡単なstd::vectorのようなクラスを自分で作成して、copy/move constructor/assignmentを定義してみたらいいかと思います。
https://github.com/Ryotaro25/leetcode_first60/pull/13

・kazukiiiさんの解説
https://github.com/Ryotaro25/leetcode_first60/pull/13#discussion_r1664897652
・右辺値参照・ムーブセマンティクス
https://cpprefjp.github.io//lang/cpp11/rvalue_ref_and_move_semantics.html
・C++ のムーブを理解する
https://zenn.dev/mafafa/articles/cba24383d46900
・How to implement our own Vector Class in C++?
https://www.geeksforgeeks.org/how-to-implement-our-own-vector-class-in-c/

**メモ(ポイントなど)**
*束縛とは?*
下記の例のように、&や&&をつけることで右辺値でのみor左辺値でのみアクセスできるように設定すること
 左辺値参照
int& lvalue_ref_1 = x; // OK
int& lvalue_ref_2 = 0; // Error 右辺値を左辺値参照で束縛している
右辺値参照
int&& rvalue_ref_1 = x; // Error 左辺値を右辺値参照で束縛している
int&& rvalue_ref_2 = 0; // OK

*ムーブされた変数は右辺値となり、それ以降使える保証はなくなる*
変数がさし示している値を付け替えるイメージだったが
ex)
std::string x = "Hello, world!";
std::string y = std::move(x);
実際はmoveを通してxを右辺値に変換して、代入が行われる
上記の後、xを無効化する(これはクラスによる)

コピーとの違いは、左辺値で受け取るのか右辺値で受け取れるのかの違い
また、moveの場合は元のオブジェクトを無効化する
48 changes: 48 additions & 0 deletions 102.BinaryTreeLevelOrderTraversal/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if (!root) {
return {};
}
map<int, vector<int>> level_to_values;

queue<NodeAndDepth> node_and_depth;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

細かいですが複数形にすると良さそうです。node_and_depths

node_and_depth.emplace(root, 0);
while (!node_and_depth.empty()) {
auto [node, depth] = node_and_depth.front();
node_and_depth.pop();

level_to_values[depth].emplace_back(node->val);
if (node->left) {
node_and_depth.emplace(node->left, depth + 1);
}
if (node->right) {
node_and_depth.emplace(node->right, depth + 1);
}
}

vector<vector<int>> values;
for (const auto& [depth, vals] : level_to_values) {
values.emplace_back(vals);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

コピーが走っていそうです。またこの場合その場でオブジェクトを構築している訳ではないので、push_backでも同じ動きになると思います。

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.

@kazukiii
レビューありがとうございます。
左辺値と右辺値で挙動が変わるのは理解しておりませんでした。ありがとうございます。
https://en.cppreference.com/w/cpp/container/vector/push_back

こちらに処理を追加したファイルを上げました(step5.cpp)。
この辺りを理解するために、前に教えていただいたvectorを自前で作ってcopy/moveにconstructor/assignmentを実装してみました(vector.cpp)。また以前いただいた資料も再度読みました。
2eeaea0

}

return values;
}

private:
struct NodeAndDepth {
TreeNode* node;
int depth;
};
};
41 changes: 41 additions & 0 deletions 102.BinaryTreeLevelOrderTraversal/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> level_to_values = {};

queue<TreeNode*> same_level_nodes;
same_level_nodes.emplace(root);
while (!same_level_nodes.empty()) {
int nodes_num = same_level_nodes.size();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

英字句にすると number of nodes になると思うので、num_nodesの方が良さそうです。

vector<int> values = {};
for (int i = 0; i < nodes_num; i++) {
auto node = same_level_nodes.front();
same_level_nodes.pop();

if (!node) {
continue;
}
values.emplace_back(node->val);
same_level_nodes.emplace(node->left);
same_level_nodes.emplace(node->right);
}

if (!values.empty()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

キューに nullptr も入れていることによってこの辺りが逆に複雑になっているような気がします。

level_to_values.emplace_back(values);
}
}

return level_to_values;
}
};
41 changes: 41 additions & 0 deletions 102.BinaryTreeLevelOrderTraversal/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> level_to_values = {};

queue<TreeNode*> same_level_nodes;
same_level_nodes.emplace(root);
while (!same_level_nodes.empty()) {
int nodes_num = same_level_nodes.size();
vector<int> values = {};
for (int i = 0; i < nodes_num; i++) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

same_level_nodesって命名微妙かなと思います
このループのなかで次のレベルのノードも入ってるので

same_level_nodesは単純なqueueにしちゃってsame_level_nodes,next_level_nodesそれぞれの配列を逐一用意してあげたほうが読みやすくなるのではと思いました

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.

@Yoshiki-Iwasa
レビューありがとうございます。
現在と次の2種のnodeが入っているので変数名正しく無いですね。それぞれを別管理にする方式に変更してみました。
step4.cppとなります。

97045e7

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.

@nittoco
レビューありがとうございます。
これは意識して気をつけようと思います🙇

auto node = same_level_nodes.front();
same_level_nodes.pop();

if (!node) {
continue;
}
values.emplace_back(node->val);
same_level_nodes.emplace(node->left);
same_level_nodes.emplace(node->right);
}

if (!values.empty()) {
level_to_values.emplace_back(values);
}
}

return level_to_values;
}
};
40 changes: 40 additions & 0 deletions 102.BinaryTreeLevelOrderTraversal/step4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> level_to_values = {};
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
effective c++の4項を読んで自分でデフォルトの動きを覚えるより、オブジェクトは初期化を必ず行う方が今の自分にはいいと思いました。
「vectorはいつでも初期化されると保証されている」とは記載ございました。

if (!root) {
return level_to_values;
}

vector<TreeNode*> current_level_nodes = {root};
while (!current_level_nodes.empty()) {
vector<int> values = {};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

C++では参照を受け取ってそこへ直接書き込むことも出来ますね。ご参考までに。
vector<int>& values = level_to_values.emplace_back();

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<TreeNode*> next_level_nodes = {};

for (const auto& node : current_level_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.

ポインタへの参照にする意味はなさそうです

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.

確かにアドレスをさし示しているので意味ないですね。。。失礼しました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

& をつけるかは、サイズや変更したいかなどで決めましょう。

values.emplace_back(node->val);
if (node->left) {
next_level_nodes.emplace_back(node->left);
}
if (node->right) {
next_level_nodes.emplace_back(node->right);
}
}
level_to_values.emplace_back(values);
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 constructorが呼ばれそうです

current_level_nodes = next_level_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.

copy assignmentが呼ばれそうです

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
レビューありがとうございます。こちらに処理を追加したファイルを上げました(step5.cpp)。
copy/move周りを理解するために、前に教えていただいたvectorを自前で作ってcopy/moveにconstructor/assignmentを実装してみました(vector.cpp)。

メモにポイントを纏めてみましたので認識に誤りございましたら指摘頂きたいです🙇
先延ばしにしておりましたがよろしくお願いいたします。
2eeaea0

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<vector>であったため、既存のvectorに置き換えて自分の定義を使うことができませんでした。なのでクラスだけ作りました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

swap を使うのも一つです。

}

return level_to_values;
}
};
40 changes: 40 additions & 0 deletions 102.BinaryTreeLevelOrderTraversal/step5.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> level_to_values = {};
if (!root) {
return level_to_values;
}

vector<TreeNode*> current_level_nodes = {root};
while (!current_level_nodes.empty()) {
vector<int> values = {};
vector<TreeNode*> next_level_nodes = {};

for (const auto node : current_level_nodes) {
values.emplace_back(node->val);
if (node->left) {
next_level_nodes.emplace_back(node->left);
}
if (node->right) {
next_level_nodes.emplace_back(node->right);
}
}
level_to_values.emplace_back(std::move(values));
current_level_nodes = std::move(next_level_nodes);
}

return level_to_values;
}
};
Loading