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
22 changes: 22 additions & 0 deletions 78. Subsets/cascading.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> all_subsets;
all_subsets.push_back({});

for (int num : nums) {
vector<vector<int>> partial_subset;
Copy link

Choose a reason for hiding this comment

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

partial_subsetは、具体的にどういうものか少し名称としてわかりにくく感じます。
この場合、numを含むsubsetなので、subset_including_numなどでしょうか?

Copy link
Owner Author

Choose a reason for hiding this comment

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

@nittoco
レビューありがとうございます。
バックトラッキング全般ですが変数名に困っており、レビューいただけるのは有り難いです。
下記で使わせていただきました。
bd2f03c

for (auto prev_subset : all_subsets) {
Copy link

Choose a reason for hiding this comment

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

auto にすると、コピーが発生します。auto& でいいでしょう。(か、下でコピーしているので、auto にしてもいいんですが、そうするとコピーが故意なのかどうかがよく分からなくなりますね。)

Copy link
Owner Author

Choose a reason for hiding this comment

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

@oda
すっかりこの視点が抜けておりました🙇毎日コードを書かないとですね。

// 一つ前の状態を取り出して、部分集合(現在のnum)を追加する
vector<int> cloned = prev_subset;
cloned.push_back(num);
partial_subset.push_back(cloned);
}
// この周回で作った集合を次の周回に渡すための処理
Copy link

Choose a reason for hiding this comment

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

all_subsets.insert(all_subsets.end(), partial_subet.begin(), partial_subset.end());
と書くこともできます。

for (auto subset : partial_subset) {
all_subsets.push_back(subset);
}
Comment on lines +16 to +18
Copy link

Choose a reason for hiding this comment

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

ここも2度コピーされています。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@oda
vectorのコピーが走りそうな部分は、emplace_backに置き換えました。
bd2f03c

Copy link

Choose a reason for hiding this comment

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

vector の中に入るところでコピーされるのはいたしかたがないので push_back でも構わないと私は思います。
emplace_back だとコンストラクターが動きますね。

}
return all_subsets;
}
};
20 changes: 20 additions & 0 deletions 78. Subsets/cascading_step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> all_subsets;
all_subsets.push_back({});

for (int num : nums) {
vector<vector<int>> partial_subset;
for (const auto& prev_subset : all_subsets) {
vector<int> cloned = prev_subset;
cloned.push_back(num);
partial_subset.emplace_back(cloned);
}
for (const auto& subset : partial_subset) {
all_subsets.emplace_back(subset);
}
}
return all_subsets;
}
};
46 changes: 46 additions & 0 deletions 78. Subsets/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## ステップ1
Permutationの回答から変形したらいけそうだと思った。
Permutationではforループで要素を順番に選びながら、再帰呼び代しで全順列を作った

ここから派生させてsubsetを作る関数にsubsetのサイズを指定することでサイズごとの全subsetを回答に追加する
Permutaionとは異なり、順番違いの同じ要素を除外する必要がある。
なんとか回答できたがGenerateSubsetに対する引数が多い=追うべきものが多すぎる

## ステップ2
変更点 step2.cpp
・含まれているのか含まれていないかのチェックを削除
 この確認をしなくてもインデックスで管理しているので、重複は発生しない

・subset_sizeと部分subsetの大きさが同じだけというチェックを削除
再帰呼び出し回数に合わせたおきさになる為不要

Leetcodeの解説より
一つ前の状態に新しい部分集合を足していく方法
cascating.cppに実装
初期 => {} 追加
nums = 1 => {1} 追加
nums = 2 => {2} {1, 2} 追加
nums = 3 => {3} {1, 3} {2, 3} {1, 2, 3}
こちらの方が自分的には理解しやすかった

## ステップ3
**3回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。**

## 他の解法
step1の直したコードに対して、includeの役割を追うのに時間がかかった
呼び出してから一連の処理を追って要約理解できたので、コメントがあってもいいかもです
https://github.com/nittoco/leetcode/pull/19/commits/964a4def0273bf53a4442f8b8908f6c4f99e022a

バックトラッキングで解いている
>バックトラックは、要するに、その場所までが固定されたときに、残りをどうやって重複や漏れがないように、分類するかということです。
https://github.com/goto-untrapped/Arai60/pull/39/commits/c8e8ee07bb84ff15a310415a6c5a551d626bd18e

indexを使って、サイズを管理している
自分もstep1の時に行なっていたが、サイズの管理は再帰関数の呼び出し回数に吸収させたい
yieldを何か調べた。一時停止させてまた再開をすると処理を追うのが大変そう。。。
あとC++にはなさそう
Copy link

Choose a reason for hiding this comment

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

https://en.cppreference.com/w/cpp/language/coroutines
C++ には coroutine が C++20 からあります。使われているのをみたことがあまりないですが。

Copy link

Choose a reason for hiding this comment

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

別の問題ですが coroutine 使った人はいます。
https://discord.com/channels/1084280443945353267/1247673286503039020/1263415894030290945

https://github.com/fhiyo/leetcode/pull/51/commits/201509ce3b67a697e84ea970c7e9e65b4b2aa00d

https://github.com/Yoshiki-Iwasa/Arai60/pull/56/commits/79711318d8fe43048883ce60f3a35e9dffbcd0ac
## Discorなど

31 changes: 31 additions & 0 deletions 78. Subsets/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> all_subsets;
vector<int> partial_subset;
all_subsets.push_back({});

for (int i = 1; i <= nums.size(); i++) {
Copy link

Choose a reason for hiding this comment

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

問題文に any order とあるため、部分集合のサイズ順に all_subsets に格納する必要はないと思います。 step2 のほうがシンプルでよいと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@nodchip
解説を読んで分かりました。チェックありがとうございます。

GenerateSubset(all_subsets, partial_subset, nums, i, 0);
}
return all_subsets;
}

private:
void GenerateSubset(vector<vector<int>>& all_subsets, vector<int>& partial_subset,
Copy link

Choose a reason for hiding this comment

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

関数の引数の並びについて、入力を先、出力を後とする流儀があります。

https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs

When ordering function parameters, put all input-only parameters before any output parameters.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@nodchip ありがとうございます。

const vector<int>& nums, int subset_size, int start_index) {
if (partial_subset.size() == subset_size) {
all_subsets.push_back(partial_subset);
return;
}

for (int i = start_index; i < nums.size(); i++) {
if (find(partial_subset.begin(), partial_subset.end(), nums[i]) != partial_subset.end()) {
continue;
}
partial_subset.push_back(nums[i]);
GenerateSubset(all_subsets, partial_subset, nums,subset_size, i + 1);
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

Choose a reason for hiding this comment

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

@nodchip
意図的にフォーマッターをエディタから消していたのですが最後のチェックで入れていてもいいかもですね🙇

partial_subset.pop_back();
}
}
};
21 changes: 21 additions & 0 deletions 78. Subsets/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> all_subsets;
vector<int> partial_subset = {};
GenerateSubset(all_subsets, 0, partial_subset, nums);
return all_subsets;
}

private:
void GenerateSubset(vector<vector<int>>& all_subsets, int start,
vector<int>& partial_subset, const vector<int>& nums) {
all_subsets.push_back(partial_subset);

for (int i = start; i < nums.size(); i++) {
Copy link

Choose a reason for hiding this comment

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

ここで for 文を回さず、 start の場所にある値を partial_subset に入れるか入れないかで分岐する方法もあります。

void GenerateSubset(const vector<int>& nums, int index, vector<int>& partial_subset, vector<vector<int>>& all_subsets) {
    if (index == nums.size()) {
        all_subsets.push_back(partial_subset);
        return;
    }

    GenerateSubset(nums, index + 1, partial_subset, all_subsets);
    partial_subset.push_back(nums[index]);
    GenerateSubset(nums, index + 1, partial_subset, all_subsets);
    partial_subset.pop_back();
}

Copy link
Owner Author

Choose a reason for hiding this comment

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

@nodchip
ありがとうございます。
選ばない場合と選ぶ場合で再帰を呼び出すのですね。
for で回す方が自分には理解しやすかったです。

partial_subset.push_back(nums[i]);
GenerateSubset(all_subsets, i + 1, partial_subset, nums);
partial_subset.pop_back();
}
}
};
21 changes: 21 additions & 0 deletions 78. Subsets/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> all_subsets;
vector<int> partial_subset = {};

Choose a reason for hiding this comment

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

default initialization で充分かと思います

Copy link
Owner Author

Choose a reason for hiding this comment

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

@austyhooong
vectorのようにいつも初期化されるものとそうでないものがあるため覚えられないため明示的に初期化しておりました。
effective c++の4項そのような記述があり、そうしておりました🙇

GenerateSubset(all_subsets, partial_subset, 0, nums);
return all_subsets;
}

private:
void GenerateSubset(vector<vector<int>>& all_subsets, vector<int>& partial_subset,

Choose a reason for hiding this comment

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

function 名をcamel かsnakeに統一した方が読みやすいのかと思いました

Copy link

Choose a reason for hiding this comment

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

C++ の場合は UpperCamel にすることが多いと思います。ただ、 LeetCode で指定されている関数名については、指定されているものをそのまま使うしかないと思います。実際には所属するチームの平均的な書き方に合わせて書くことをお勧めいたします。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@austyhooong @nodchip
レビューありがとうございます。
C++はこの会以外で使ったことがなく、会社でも使っていないため下記のグーグルガイドを参考にしております。
https://google.github.io/styleguide/cppguide.html#Function_Names

int start, const vector<int>& nums) {
all_subsets.push_back(partial_subset);

for (int i = start; i < nums.size(); i++) {
partial_subset.push_back(nums[i]);
GenerateSubset(all_subsets, partial_subset, i + 1, nums);
partial_subset.pop_back();
}
}
};
22 changes: 22 additions & 0 deletions 78. Subsets/step4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> all_subsets;
vector<int> subset_including_num = {};
GenerateSubset(nums, 0, subset_including_num, all_subsets);
return all_subsets;
}

private:
void GenerateSubset(const vector<int>& nums, int start, vector<int>& subset_including_num,
vector<vector<int>>& all_subsets
) {
all_subsets.emplace_back(subset_including_num);

for (int i = start; i < nums.size(); i++) {
subset_including_num.push_back(nums[i]);
GenerateSubset(nums, i + 1, subset_including_num, all_subsets);
subset_including_num.pop_back();
}
}
};