Skip to content
Open
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
94 changes: 94 additions & 0 deletions 46/46.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
## 何も見ずに解いてみる

```cpp
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
if (nums.size() == 0) {
result.push_back({});
}
for (int i = 0; i < nums.size(); ++i) {
vector<int> nums_copy = nums;
nums_copy.erase(nums_copy.begin() + i);
for (auto& vec : permute(nums_copy)) {
vec.insert(vec.begin(), nums[i]);
result.push_back(vec);
}
}
return result;
}
};
```

## 他の人のコードを見てみる

https://github.com/tokuhirat/LeetCode/pull/50/files
https://github.com/ryosuketc/leetcode_arai60/pull/39/files
https://github.com/Ryotaro25/leetcode_first60/pull/54/files

swapを使って書いたコードは、下のコードを

- sub_permutationをnumsのdecided_untilより前として管理する
- unused_numsをnumsのdecided_until以降として管理する

ことでより早くしたカッコイイバージョンだと捉えられる気がしますね。
Geminiによるとメモリ上で違う位置にあるsub_permutationとunused_numsをいちいち反復横跳びしなくてよくなっているという点でも高速化に貢献しているらしいです。(真偽のほどは検証していません・・・)

```cpp
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
vector<int> initial_vector = {};
unordered_set<int> nums_set(nums.begin(), nums.end());
permuteHelper(result, initial_vector, nums_set);
return result;
}
private:
void permuteHelper(vector<vector<int>>& result, vector<int>& sub_permutation, unordered_set<int>& unused_nums) {
if (unused_nums.empty()) {
result.emplace_back(sub_permutation);
return;
}
// unused_numsでそのまま回すとfor文の最中にいじってるのが原因でエラー吐くのでコピーして回します
unordered_set<int> unused_nums_copy(unused_nums.begin(), unused_nums.end());
for (int num : unused_nums_copy) {
unused_nums.erase(num);
sub_permutation.emplace_back(num);
permuteHelper(result, sub_permutation, unused_nums);
sub_permutation.pop_back();
unused_nums.insert(num);
}
}
};
```

resultにpush_backとかemplace_backするとき順列をコピーしてから入れなくて大丈夫なのかと思ったんですが、オブジェクトそのものではなくてコンストラクタの引数として渡されてそれをもとに新しい要素が作られるという挙動なのでいらないみたいです。pythonのlist.appendとは違いますね。

## 最終コード

```cpp
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
permuteHelper(result, nums, 0);
return result;
}
void permuteHelper(vector<vector<int>>& result, vector<int>& nums, int decided_until) {

Choose a reason for hiding this comment

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

細かいですが、private 忘れでしょうか。

Copy link
Owner Author

Choose a reason for hiding this comment

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

あ、失念していました。

Choose a reason for hiding this comment

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

private でも問題ないでしょうか。

results は出力なので引数の最後にした方が良いかもしれません。
https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs

Copy link
Owner Author

Choose a reason for hiding this comment

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

privateで良いと思います。入れるのを忘れてしまっていました。
出力を最後に持ってくるのが推奨されているというのは知りませんでした。ありがとうございます。

if (decided_until == nums.size()) {
result.emplace_back(nums);
return;
}
for (int i = decided_until; i < nums.size(); ++i) {
swap(nums[decided_until], nums[i]);
permuteHelper(result, nums, decided_until + 1);
swap(nums[decided_until], nums[i]);

Choose a reason for hiding this comment

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

decided_until は swap_index のように感じました。ここまで決めたんだという意味は落ちてしまいますが、、、

}
}
};

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.

たしかに、その視点はありませんでした。setを使った解法は動かないように思います。

```

そういえばこれnums参照渡しで渡されてるので(最終的に戻ってくるときは元の状態で戻ってきますが)関数が走ってるとき中身の要素がめっちゃswapされることになると思うんですが、大丈夫なんでしょうか。
numsが変化したときにそれを何らかの方法で検出して何か操作を行う、みたいな仕組みが組まれてると怪しいかもしれません。コピーしたほうが安心ですかね?