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
30 changes: 30 additions & 0 deletions 31.NextPermutation/lower_bound.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int left = FindDecreasingIndex(nums);
// nums全体が降順になっている場合
if (left == -1) {
reverse(nums.begin(), nums.end());
return;
}
int right = FindRightmostLarger(nums, left);
swap(nums[left], nums[right]);
reverse(nums.begin() + left + 1, nums.end());
}

private:
// 右側が降順になっている範囲の直前のインデックスを探す
int FindDecreasingIndex(const vector<int>& nums) {
Copy link

Choose a reason for hiding this comment

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

nextPermutationとこちらの関数名のネーミングルールが揃っていないことに違和感があります。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@usatie
レビューありがとうございます。
この会では可能な限りGoogleのスタイルガイドに合わせてコードを書いております。これはこの会以外ではC++を使うことがないためです。

全体で統一できればなのですが、LeetCodeで用意されている部分は仕方ないかなと思っております。
https://google.github.io/styleguide/cppguide.html#Function_Names

int index = nums.size() - 2;
while (index >= 0 && nums[index] >= nums[index + 1]) {
index--;
}
return index;
}

// 大きい値のうち、最も右側のものを見つける
int FindRightmostLarger(vector<int>& nums, int left) {
auto it = lower_bound(nums.begin() + left + 1, nums.end(), nums[left], greater<int>());
return distance(nums.begin(), it) - 1;
}
};
79 changes: 79 additions & 0 deletions 31.NextPermutation/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
## ステップ1
>if all the permutations of the array are sorted in one container according to their lexicographical order, then the next permutation of that array is the permutation that follows it in the sorted container.
[1,2,3]の次は[1,3,2]ということ
>If such arrangement is not possible, the array must be rearranged as the lowest possible order
[3,2,1]の次は[1,2,3]に戻るということ

これだけだと分からないので
[1,2,3,4]で考える
[1,2,4,3]
[1,3,2,4]
[1,3,4,2]
[1,4,2,3]
[1,4,3,2]
[2,1,3,4]
.
.
.
20分ほど考えて分からなかったので、回答を見る
前に、next_permutationを使った際に実装方法まで見ておけばよかった
https://en.cppreference.com/w/cpp/algorithm/next_permutation

次の順列を作る流れは以下の通りとなっている。
1.後ろから探索して、numsの中で最初に、前から昇順に並んでいるインデックスを探す
2.1のインデックスに対応する値より大きい、最も右にある要素を探す
3.1と2の要素を入れ替える
4.1.のインデックス以降を昇順にする(元々は降順)


## ステップ2
・first_increasing_order_index
この変数名から何をしたいのか分かりにくいのと、
実際はfirst_increasing_order_index - 1として使っていることが多いため
単にpivotとかでも良さそう

・next_larger_index
nextよりrightmostの方がまだ良さそう
コメントがないと何をしているのか伝わらない
コメントも微妙なので、改善等あれば言って頂けると助かります。

・reverse
pivot以降を逆順に並び変える箇所について
元々pivot以降は降順に並んでいるので、reverseを使い結果として昇順に並び替えているが
sortを使った方がわかりやすいような気がする
O(n)からO(n log n)になるが、昇順に並び替えているというのは伝わりそう

next_permutation関数を使うのは、求めらていないだろう

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

Choose a reason for hiding this comment

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

なんとなく解くことが目的化しているというか、きれいなコードを GitHub に上げることが目的になっている感触を微かに持っています。(オンラインなのではっきりとは分かりません。)
この部分は、私がここで練習して欲しいことの2,3割です。
私が訓練したいのは、反応をするようになるとか感情を持てるようになるなどの内心です。
参加マニュアルを一回読み直してみてください。
http://docs.google.com/document/d/1bjbOSs-Ac0G_cjVzJ2Qd8URoU_0BNirZ8utS3CUAeLE

ただ、好きなようにやればいいとは思います。

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
一度マニュアルを読み直します。


## 他の方の解法

自分の知らない関数をいくつか使われていて勉強になる
https://en.cppreference.com/w/cpp/algorithm/adjacent_find
https://en.cppreference.com/w/cpp/algorithm/iter_swap

>// Find the first DSC pair from the tail : [4,(5,7),6,3,2,1]
>// to point to the second element of the pair :
>// [4,(*5,7),6,3,2,1]
この辺りのコメントの書き方が分かりやすいと思った。

iteratorを使って処理することでコードを綺麗に纏められそう
https://github.com/usatie/leetcode/pull/2/commits/f0875e0db129eb5eabcb5fd6363de8258befe3ce


>引数は広い型で指定した方が使える場面が増えるかなと思って、一応そうしてみてます。
目先の問題に囚われないで実務を想定している。この考え方なるほどです。

二分探索の選択肢は持っておく
lower_bound.cppに実装
https://github.com/fhiyo/leetcode/pull/56/commits/6836d3c8ee01216ccd303dc5df93b199311a4e18

>実際はleftより後ろの要素のうち、nums[left]より大きい一番後ろの要素のindexを返しているので
確かに処理に対して適切な名前を付けるは難しい
関数に分けて、関数名や引数から処理を予想させるは良さそう
https://github.com/Mike0121/LeetCode/pull/15/commits/cac8eb6c5ecc3db4644cb5f9b34c7046238b53e1

## Discordなど

22 changes: 22 additions & 0 deletions 31.NextPermutation/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int first_increasing_order_index = nums.size() - 1;
while (first_increasing_order_index > 0) {
if (nums[first_increasing_order_index - 1] < nums[first_increasing_order_index]) {
break;
}
first_increasing_order_index--;
}
Comment on lines +4 to +10
Copy link

Choose a reason for hiding this comment

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

これを見て、first_increasing_order_index の初期化がここで終了したという理解は構造からは得られないと思うんですよね。たとえば、下はどうでしょうか。

int first_increasing_order_index = 0;
for (int i = num.size() - 1; i > 0; --i) {
    if (...) {
       first_increasing_order_index = i;
        break;
    }
}

これは、0 がデフォルトで後ろから条件を満たすものを探して初期化したと読めますね。

const int first_increasing_order_index = get_first_increasing_order(nums);

これも初期化したと読めますね。

初期化するために繰り返し変更すると意図が追いにくくなるでしょう。

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

初期化するために繰り返し変更すると意図が追いにくくなるでしょう。
改めて読むと何のための処理なのかコードから追えない、レビュワー(ユーザー)にとって優しくないコードでした。

きれいなコードを GitHub に上げることが目的になっている感触を微かに持っています。
この部分も含めコードを書き直して、step4に上げてみました🙇
7c97dc1

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.

@oda なるほどです。メタ認知力を鍛えたいと思ってそう言ったタイトルの本を読んでみたのですが、おすすめの本などあったりしますでしょうか?

Copy link

Choose a reason for hiding this comment

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

よく分かりません。
いや、まあ、でも一般的に国語力といわれるものが大事ですかねえ。


if (first_increasing_order_index > 0) {
int next_larger_index = nums.size() - 1;
while (nums[next_larger_index] <= nums[first_increasing_order_index - 1]) {
next_larger_index--;
}
swap(nums[first_increasing_order_index - 1], nums[next_larger_index]);
}

reverse(nums.begin() + first_increasing_order_index, nums.end());
}
};
24 changes: 24 additions & 0 deletions 31.NextPermutation/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int pivot_index = nums.size() - 1;

// numsを後ろから探索して昇順となっている箇所をpivotとする
while (pivot_index > 0 && nums[pivot_index - 1] >= nums[pivot_index]) {
pivot_index--;
}

// 先頭から見てnums全てが降順に並んでいる場合は対象外
// pivot_index - 1より大きく、最も右側の位置に存在している値を探して
// pivot_index - 1と入れ替える
if (pivot_index > 0) {
int rightmost_larger_index = nums.size() - 1;
while (nums[rightmost_larger_index] <= nums[pivot_index - 1]) {
rightmost_larger_index--;
}
swap(nums[pivot_index - 1], nums[rightmost_larger_index]);
}

sort(nums.begin() + pivot_index, nums.end());
}
};
19 changes: 19 additions & 0 deletions 31.NextPermutation/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int pivot_index = nums.size() - 1;
while (pivot_index > 0 && nums[pivot_index - 1] >= nums[pivot_index]) {
pivot_index--;
}

if (pivot_index > 0) {
int rightmost_larger_index = nums.size() - 1;
while (nums[rightmost_larger_index] <= nums[pivot_index - 1]) {
rightmost_larger_index--;
}
swap(nums[pivot_index - 1], nums[rightmost_larger_index]);
}

sort(nums.begin() + pivot_index, nums.end());
}
};
25 changes: 25 additions & 0 deletions 31.NextPermutation/step4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class Solution {
public:
void nextPermutation(vector<int>& nums) {
// 後ろから探索して、numsの中で最初に前から昇順に並んでいるインデックスを探す
int first_increasing_order_index = 0;
for (int i = nums.size() - 1; i > 0; i--) {
if (nums[i - 1] < nums[i]) {
first_increasing_order_index = i;
break;
}
}

if (first_increasing_order_index > 0) {
// first_increasing_order_index より大きい値を持つ最も右の要素を探す
for (int i = nums.size() - 1; i >= first_increasing_order_index; i--) {
if (nums[i] > nums[first_increasing_order_index - 1]) {
swap(nums[first_increasing_order_index - 1], nums[i]);
break;
}
}
}

reverse(nums.begin() + first_increasing_order_index, nums.end());
}
};