-
Notifications
You must be signed in to change notification settings - Fork 0
31. Next Permutation #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: review-base
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| NAME = a.out | ||
| SRC = main.cpp | ||
| CXX = g++ | ||
| CXXFLAGS = -Wall -Wextra -Werror -std=c++11 | ||
|
|
||
| all: $(NAME) | ||
|
|
||
| $(NAME): $(SRC) step3.hpp | ||
| $(CXX) -o $(NAME) $(SRC) $(CXXFLAGS) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| #include "step3.hpp" | ||
| #include <iostream> | ||
| #include <vector> | ||
|
|
||
| #define T(...) \ | ||
| [&] { \ | ||
| std::cout << #__VA_ARGS__ " -> "; \ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 可変引数マクロですね。確かに、そのまま文字列化するならば、こうするしかないですが、逆に変数を引数に与えると ({;;}) は、GCC 拡張だったかと思います。 単に {;;} とするのも手ですが、{;;}; と展開されるので、 if () T(); else {}で事故りそうです。 ラムダの呼び出しに変えるのは一つです。[&]{std::cout << "";}() テスト目的なのでよいという考えもありますが、結構嵌りそうなコードと感じました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 今回は以前使ったことがあった 返り値が最後の式の評価になってしまうとは知りませんでした。単純に
|
||
| std::vector<int> nums{__VA_ARGS__}; \ | ||
| solution.nextPermutation(nums); \ | ||
| print_arr(nums); \ | ||
| }() | ||
| void print_arr(std::vector<int> &nums) { | ||
| std::cout << "["; | ||
| for (auto it = nums.begin(), end = nums.end(); it != end; ++it) { | ||
| std::cout << *it; | ||
| if (it + 1 != nums.end()) { | ||
| std::cout << ","; | ||
| } | ||
| } | ||
| std::cout << "]" << std::endl; | ||
| } | ||
|
|
||
| int main() { | ||
| Solution solution; | ||
| T({1, 2, 3}); | ||
| T({1, 3, 2}); | ||
| T({3, 2, 1}); | ||
| T({1, 1, 5}); | ||
| T({4, 5, 7, 6, 3, 2, 1}); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| /* | ||
| 何がわからなかったか | ||
| - 次のpermutationを求めるためのステップの着想を得るのが難しかった | ||
|
|
||
| 何を考えて解いていたか | ||
| 1. From the tail, find the first asc pair (i, i+1) | ||
| 2. find j where nums[j] > nums[i] and j in (i+1, tail) | ||
| 3. swap(i, j) | ||
| 4. sort(i+1, tail) | ||
|
|
||
| 正解してから気づいたこと | ||
| - algorithm系(lower_bound, upper_bound, findなど)を使えば短く綺麗に書けそう | ||
| - いつもalgorithm系はcppreferenceを見て、何度か失敗しながらじゃないと書けない | ||
| - 後ろからiterateするにはrbeginとrendを使えばよさそう | ||
| */ | ||
| class Solution { | ||
| public: | ||
| void nextPermutation(vector<int> &nums) { | ||
| // [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] | ||
| // ... | ||
|
|
||
| // 1. From the tail, find the first asc pair (i, i+1) | ||
| // 2. find j where nums[j] > nums[i] and j in (i+1, tail) | ||
| // 3. swap(i, j) | ||
| // 4. sort(i+1, tail) | ||
|
|
||
| int tail = nums.size() - 1; | ||
| int i, j; | ||
| // 1. Find the first ASC pair nums[i] and nums[i+1] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 略語は読むにあたり、やや認知負荷が高く感じます。単語はできる限りフルスペルで書いたほうが良いと思います。 |
||
| bool found = false; | ||
| for (i = tail - 1; i >= 0; --i) { | ||
| if (nums[i] < nums[i + 1]) { | ||
| found = true; | ||
| break; | ||
| } | ||
| } | ||
| if (!found) { | ||
| std::sort(nums.begin(), nums.end()); | ||
| return; | ||
| } | ||
| // 2. Find j | ||
| found = false; | ||
| for (j = tail; j >= i + 1; --j) { | ||
| if (nums[j] > nums[i]) { | ||
| found = true; | ||
| break; | ||
| } | ||
| } | ||
| if (!found) { | ||
| std::sort(nums.begin(), nums.end()); | ||
| return; | ||
| } | ||
| // 3. swap | ||
| std::swap(nums[i], nums[j]); | ||
| // 4. sort | ||
| std::sort(nums.begin() + i + 1, nums.end()); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /* | ||
| 講師陣はどのようなコメントを残すだろうか? | ||
| 他の人のコードを読んで考えたこと | ||
| - https://github.com/Yoshiki-Iwasa/Arai60/pull/63/files | ||
| 1. Rustのlet Some, unreachable!マクロわかりやすくていい | ||
| 2. pivotやsuccessorの名前がわかりやすい | ||
| 3. pivotやsuccessorを見つける際がワンライナーで書けている。 | ||
| C++だとどう書けるだろうか。 | ||
| 4. rpositionに当たるような右から見ていく関数はあっただろうか | ||
| https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.rposition | ||
| - https://github.com/fhiyo/leetcode/pull/56/files | ||
| 1. 二重ループのコードは計算量的に無駄があるし、何をしているのか | ||
| 自分としては少しわかりにくいと感じたが、なるほど確かに短くは書けるのか。 | ||
| 2. 関数や変数名についてみんな迷っていたが、pivot, successorなどのようにその | ||
| 変数がそもそも何なのか、概念を見つけられれば名前がつけやしのかもしれない。 | ||
| そうではなく、手続きややっていることを説明しようとすると、長くてわかりに | ||
| くくなってしまうのだろう。 | ||
| (そもそもprogram自体が指示の羅列なのだから読めばわかるもの) | ||
|
|
||
| 改善する時にかんがえたこと | ||
| 1. algorithm関連 : cppreferenceを見てみて、使えそうな関数を探してみる | ||
| https://en.cppreference.com/w/cpp/algorithm/find | ||
| https://en.cppreference.com/w/cpp/algorithm/find_end | ||
| https://en.cppreference.com/w/cpp/algorithm/adjacent_find | ||
| https://en.cppreference.com/w/cpp/algorithm/lower_bound | ||
| https://en.cppreference.com/w/cpp/algorithm/upper_bound | ||
| https://en.cppreference.com/w/cpp/algorithm/iter_swap | ||
| 2. Iterator関連 : cppreferenceを見てみて、使えそうな関数を探してみる | ||
| https://en.cppreference.com/w/cpp/iterator/rbegin | ||
| https://en.cppreference.com/w/cpp/iterator/rend | ||
| https://en.cppreference.com/w/cpp/iterator/prev | ||
| https://en.cppreference.com/w/cpp/iterator/next | ||
| https://en.cppreference.com/w/cpp/iterator/advance | ||
| https://en.cppreference.com/w/cpp/iterator/distance | ||
| 3. rbeginとrendを使えば後ろから舐めていける | ||
| 4. adjacent_findとupper_boundが今回の例にはピッタリ | ||
| 5. reverse(begin, pivot)としておけば最初に降順ペアが見つからなかった場合にも | ||
| 正しく動作するのでearly returnしなくても一箇所にまとめられる。 | ||
| */ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://en.cppreference.com/w/cpp/algorithm/next_permutation
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほどこんなものが標準ライブラリで提供されていたんですね。 以下のように使えるとchatgptに教えてもらってなるほど、確かにこれがあると使いやすいなと理解できました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 以下の二つについても使ったことがなかったので、参考実装を見て勉強になりました。 std::make_reverse_iterator std::is_sorted_until |
||
| class Solution { | ||
| public: | ||
| void nextPermutation(std::vector<int> &nums) { | ||
| // Find the first DSC pair from the tail : [4,(5,7),6,3,2,1] | ||
| auto pivot = | ||
| std::adjacent_find(nums.rbegin(), nums.rend(), std::greater<int>()); | ||
| if (pivot != nums.rend()) { | ||
| ++pivot; // to point to the second element of the pair : | ||
| // [4,(*5,7),6,3,2,1] | ||
| // to find the successor of pivot : [4,(*5,7),**6,3,2,1] | ||
| auto successor = std::upper_bound(nums.rbegin(), pivot, *pivot); | ||
| std::iter_swap(pivot, successor); | ||
| } | ||
| std::reverse(nums.rbegin(), pivot); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| /* | ||
| 3問連続で正解するのにかかった時間 3:28 | ||
|
|
||
| 時間計算量: O(N) | ||
| 空間計算量: O(1) | ||
| */ | ||
| #ifndef STEP3_H | ||
| #define STEP3_H | ||
| #include <algorithm> | ||
| #include <iostream> | ||
| #include <vector> | ||
| class Solution { | ||
| public: | ||
| void nextPermutation(std::vector<int> &nums) { | ||
| auto pivot = | ||
| std::adjacent_find(nums.rbegin(), nums.rend(), std::greater<int>()); | ||
| if (pivot != nums.rend()) { | ||
| ++pivot; | ||
| auto successor = std::upper_bound(nums.rbegin(), pivot, *pivot); | ||
| std::iter_swap(pivot, successor); | ||
| } | ||
| std::reverse(nums.rbegin(), pivot); | ||
| } | ||
| }; | ||
|
|
||
| /* | ||
| 自作してみたデータ構造や関数 | ||
| */ | ||
| namespace playground { | ||
| // https://en.cppreference.com/w/cpp/algorithm/adjacent_find | ||
| // 初めて使ったため | ||
| template <class ForwardIt, class BinaryPred> | ||
| ForwardIt adjacent_find(ForwardIt first, ForwardIt last, BinaryPred p) { | ||
| if (first == last) | ||
| return last; | ||
| ForwardIt next = first; | ||
| ++next; | ||
| while (next != last) { | ||
| if (p(*first, *next)) | ||
| return first; | ||
| ++next; | ||
| ++first; | ||
| } | ||
| return last; | ||
| } | ||
|
|
||
| // https://en.cppreference.com/w/cpp/algorithm/upper_bound | ||
| // Searches for the first element in the partitioned range [first, last) which | ||
| // is ordered AFTER value. いつもlower_boundと使い方を迷うため | ||
| template <class ForwardIt, | ||
| class T = typename std::iterator_traits<ForwardIt>::value_type> | ||
| ForwardIt upper_bound(ForwardIt first, ForwardIt last, const T &value) { | ||
| return upper_bound(first, last, value, std::less<T>()); | ||
| } | ||
|
|
||
| template <class ForwardIt, | ||
| class T = typename std::iterator_traits<ForwardIt>::value_type, | ||
| class Compare> | ||
| ForwardIt upper_bound(ForwardIt first, ForwardIt last, const T &value, | ||
| Compare comp) { | ||
| auto lo = first, hi = last; | ||
| while (lo < hi) { | ||
| auto mid = lo + (hi - lo) / 2; | ||
| if (!comp(value, *mid)) { | ||
| // *mid <= value | ||
| // i.e. !(*mid > value) | ||
| lo = mid + 1; | ||
| } else { | ||
| hi = mid; | ||
| } | ||
| } | ||
| return lo; | ||
| } | ||
|
|
||
| // https://en.cppreference.com/w/cpp/algorithm/iter_swap | ||
| // 初めて使った | ||
| template <class ForwardIt1, class ForwardIt2> | ||
| void iter_swap(ForwardIt1 a, ForwardIt2 b) { | ||
| std::swap(*a, *b); | ||
| } | ||
|
|
||
| // https://en.cppreference.com/w/cpp/algorithm/reverse | ||
| template <class BidirIt> void reverse(BidirIt first, BidirIt last) { | ||
| --last; | ||
| while (first < last) { | ||
| std::swap(*first, *last); | ||
| ++first; | ||
| --last; | ||
| } | ||
| } | ||
|
|
||
| } // namespace playground | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ローカルの実行環境はLeetCodeに合わせるのはどうでしょうか?
https://support.leetcode.com/hc/en-us/articles/360011833974-What-are-the-environments-for-the-programming-languages
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます。なるほどそういう手がありましたか・・・!やってみます!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
自分はローカルの環境構築をdevenv.shで行っているのですが、1時間ほどこの設定にするために時間を溶かしてしまってそれでもまだできなさそうなので、一旦諦めます><