From 603ea478c8b7c1b98a9b4d4e3ca05cad7e960f9e Mon Sep 17 00:00:00 2001 From: Ryotaro Kurita Date: Sun, 23 Mar 2025 18:48:46 +0900 Subject: [PATCH 1/2] finish --- 31.NextPermutation/lower_bound.cpp | 30 ++++++++++++ 31.NextPermutation/memo.md | 79 ++++++++++++++++++++++++++++++ 31.NextPermutation/step1.cpp | 22 +++++++++ 31.NextPermutation/step2.cpp | 24 +++++++++ 31.NextPermutation/step3.cpp | 19 +++++++ 5 files changed, 174 insertions(+) create mode 100644 31.NextPermutation/lower_bound.cpp create mode 100644 31.NextPermutation/memo.md create mode 100644 31.NextPermutation/step1.cpp create mode 100644 31.NextPermutation/step2.cpp create mode 100644 31.NextPermutation/step3.cpp diff --git a/31.NextPermutation/lower_bound.cpp b/31.NextPermutation/lower_bound.cpp new file mode 100644 index 0000000..0c8421a --- /dev/null +++ b/31.NextPermutation/lower_bound.cpp @@ -0,0 +1,30 @@ +class Solution { + public: + void nextPermutation(vector& 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& nums) { + int index = nums.size() - 2; + while (index >= 0 && nums[index] >= nums[index + 1]) { + index--; + } + return index; + } + + // 大きい値のうち、最も右側のものを見つける + int FindRightmostLarger(vector& nums, int left) { + auto it = lower_bound(nums.begin() + left + 1, nums.end(), nums[left], greater()); + return distance(nums.begin(), it) - 1; + } +}; diff --git a/31.NextPermutation/memo.md b/31.NextPermutation/memo.md new file mode 100644 index 0000000..538e967 --- /dev/null +++ b/31.NextPermutation/memo.md @@ -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回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。** + +## 他の方の解法 + +自分の知らない関数をいくつか使われていて勉強になる +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など + diff --git a/31.NextPermutation/step1.cpp b/31.NextPermutation/step1.cpp new file mode 100644 index 0000000..2aac2a2 --- /dev/null +++ b/31.NextPermutation/step1.cpp @@ -0,0 +1,22 @@ +class Solution { + public: + void nextPermutation(vector& 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--; + } + + 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()); + } + }; diff --git a/31.NextPermutation/step2.cpp b/31.NextPermutation/step2.cpp new file mode 100644 index 0000000..d303edc --- /dev/null +++ b/31.NextPermutation/step2.cpp @@ -0,0 +1,24 @@ +class Solution { + public: + void nextPermutation(vector& 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()); + } + }; diff --git a/31.NextPermutation/step3.cpp b/31.NextPermutation/step3.cpp new file mode 100644 index 0000000..d321336 --- /dev/null +++ b/31.NextPermutation/step3.cpp @@ -0,0 +1,19 @@ +class Solution { + public: + void nextPermutation(vector& 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()); + } + }; From 7c97dc1e4807733c312e7b34aa1376fce38d7d44 Mon Sep 17 00:00:00 2001 From: Ryotaro Kurita Date: Mon, 31 Mar 2025 23:43:15 +0900 Subject: [PATCH 2/2] mod code reflecting review --- 31.NextPermutation/memo.md | 2 +- 31.NextPermutation/step4.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 31.NextPermutation/step4.cpp diff --git a/31.NextPermutation/memo.md b/31.NextPermutation/memo.md index 538e967..6528c89 100644 --- a/31.NextPermutation/memo.md +++ b/31.NextPermutation/memo.md @@ -41,7 +41,7 @@ https://en.cppreference.com/w/cpp/algorithm/next_permutation pivot以降を逆順に並び変える箇所について 元々pivot以降は降順に並んでいるので、reverseを使い結果として昇順に並び替えているが sortを使った方がわかりやすいような気がする - o(n)からO(n log n)になるが、昇順に並び替えているというのは伝わりそう + O(n)からO(n log n)になるが、昇順に並び替えているというのは伝わりそう next_permutation関数を使うのは、求めらていないだろう diff --git a/31.NextPermutation/step4.cpp b/31.NextPermutation/step4.cpp new file mode 100644 index 0000000..1876886 --- /dev/null +++ b/31.NextPermutation/step4.cpp @@ -0,0 +1,25 @@ +class Solution { + public: + void nextPermutation(vector& 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()); + } + };