-
Notifications
You must be signed in to change notification settings - Fork 0
206. reverse linked list #7
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: main
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,177 @@ | ||
| # step1 | ||
| - スタックのセクションにあったのでとりあえずスタックで解いた。 | ||
| - 素直に書いてガチャガチャ変えて動いてしまった。よくない。 | ||
| ```cpp:step1.cpp | ||
| #include <stack> | ||
|
|
||
| class Solution { | ||
| public: | ||
| ListNode* reverseList(ListNode* head) { | ||
| std::stack<ListNode*> st; | ||
| ListNode* tail = head; | ||
| // nullをはじくのを統一的に書けないか? | ||
| if (head == nullptr) { | ||
| return nullptr; | ||
| } | ||
| while(tail != nullptr) { | ||
| st.push(tail); | ||
| tail = tail->next; | ||
| } | ||
| head = st.top(); | ||
|
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. ざっと読んだ時に、この行を読み飛ばしていて、
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. ありがとうございます。おっしゃる通り、最初書いたときは引数のheadを戻り値に流用していたのですが、一つのプログラム内で変数の意味合いが変わるのは紛らわしいと思いやめました。 |
||
| st.pop(); | ||
| tail = head; | ||
| while(!st.empty()) { | ||
| tail->next = st.top(); | ||
| st.pop(); | ||
| tail = tail->next; | ||
| } | ||
| // これってなんで必要? | ||
| tail->next = nullptr; | ||
|
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. これがないとreverseした最後のNodeと最後から二番目のNodeがお互いを指してcycleになる気がしますね |
||
| return head; | ||
| } | ||
| }; | ||
|
|
||
| ``` | ||
|
|
||
| # step2 | ||
| - 再帰で書けることに気づく。 | ||
| ```cpp:step2.cpp | ||
| class Solution { | ||
| public: | ||
| ListNode* reverseList(ListNode* head) { | ||
| return reverseRecursively(head); | ||
| } | ||
| private: | ||
| // node以降のリストを逆順に繋ぎなおし、元々の最後尾を新たなheadとして返す | ||
| ListNode* reverseRecursively(ListNode* node) { | ||
| if (node == nullptr || node->next == nullptr) { | ||
| return node; | ||
| } | ||
| ListNode* head = reverseRecursively(node->next); | ||
|
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. スペースは 1 つで十分だと思います。 |
||
| node->next->next = node; | ||
|
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. これは個人の感想で他の方々の意見を伺いたいですが、nextのnextにアクセスするのって頭の中で画を想像しづらくて結構トリッキーだと感じるんですよね。返り値で(reversed_head, reversed_tail)の二つを返すか、引数で(reversing, appending)を受け取る方法 (参考)の方がなんとなく想像しやすい解法のような気がします。
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. たしかにうまく再帰する部分問題に分けれていない感じがありますね…ご参考で頂いた書き方がスタイリッシュでいいですね! // [1,2,3], [4,5,6] -> [3,2,1,4,5,6]
// [], [4,5,6] -> [4,5,6]
ListNode* reverse_and_append(ListNode* head1, ListNode* head2){
if (head1 == nullptr) {
return head2;
}
auto new_head1 = head1->next;
head1->next = head2;
return reverse_and_append(new_head1, head1);
} |
||
| node->next = nullptr; | ||
| return head; | ||
| } | ||
| }; | ||
| ``` | ||
| 人のコードを見る。 | ||
| - step1ですっきりしなかった部分も番兵を使うときれいに書ける([linoahhhさん](https://github.com/lilnoahhh/leetcode/pull/10/files/31a6eddb4e971614db2b6d2fb67da1a44fb764c4#r1948006046))。 | ||
| - スタックに入っているノードを取り出してつなげる、というのに最後の場合分けが生じなくてよくなる。 | ||
| ```cpp | ||
| #include <stack> | ||
|
|
||
| class Solution { | ||
| public: | ||
| ListNode* reverseList(ListNode* head) { | ||
| std::stack<ListNode*> st; | ||
| ListNode* sentinel = nullptr; | ||
| st.push(sentinel); | ||
| ListNode* tail = head; | ||
| while (tail) { | ||
| st.push(tail); | ||
| tail = tail->next; | ||
| } | ||
|
|
||
| ListNode* new_head = st.top();st.pop(); | ||
| tail = new_head; | ||
| while (!st.empty()) { | ||
| tail->next = st.top(); | ||
| st.pop(); | ||
| tail = tail->next; | ||
| } | ||
| return head; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
|
|
||
| - もっと素直に前後を入れ替えていく([tarinaihitoriさん](https://discord.com/channels/1084280443945353267/1295357747545505833/1298524551592018003)) | ||
| ```cpp:step2_another.cpp | ||
| class Solution { | ||
| public: | ||
| ListNode* reverseList(ListNode* head){ | ||
| // 反転済みリストの先頭 | ||
| ListNode* reversed_head = nullptr; | ||
| ListNode* cur_node = head; | ||
|
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. cur_node という変数名は読む助けにほとんどならないのでもう少し良いものが欲しいですね。 「変数名をつける」という行為も、比較的小さいものであるにしても技術的選択であり、その技術的選択はなんらかの意味でエンジニアリングに繋げて判断する、ということです。
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. ありがとうございます。いまいちcur_nodeがよくないという感覚が持てなかったのですが、現にこのコードで直後に変数名を間違えており意味のある命名の必要性を感じました。 |
||
| while (cur_node) { | ||
| ListNode* tmp = cur_node->next; | ||
| tail->next = reversed_head; | ||
| reversed_head = cur_node; | ||
| cur_node = tmp; | ||
| } | ||
| return reversed_head; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| シンプルな問だけどけっこう方針が違う解法があって面白い。 | ||
|
|
||
|
|
||
| # step3 | ||
| スタック | ||
| ```cpp:step3.cpp | ||
| #include <stack> | ||
| class Solution { | ||
| public: | ||
| ListNode* reverseList(ListNode* head) { | ||
| std::stack<ListNode*> st; | ||
| auto sentinel = nullptr; | ||
| st.push(sentinel); | ||
|
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. 単にst.push(nullptr)でもよい気がします
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. たしかに変数を置かずともコメントで十分ですね。 |
||
| auto tail = head; | ||
| while (tail) { | ||
| st.push(tail); | ||
| tail = tail->next; | ||
| } | ||
|
|
||
| auto new_head = st.top(); | ||
| st.pop(); | ||
| tail = new_head; | ||
| while (tail) { | ||
| tail->next = st.top(); | ||
| st.pop(); | ||
| tail = tail->next; | ||
| } | ||
| return new_head; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| シンプルな交換 | ||
| ```cpp | ||
| class Solution { | ||
| public: | ||
| ListNode* reverseList(ListNode* head) { | ||
| ListNode* reversed_head = nullptr; | ||
| ListNode* cur_node = head; | ||
| while (cur_node) { | ||
| auto tmp = cur_node->next; | ||
|
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. tmpという名前だけだと、cur_nodeにtmpを代入するときにあれtmpってなんだったっけ、とこの行に戻って確認する必要が出てきてしまう気がするので、next_nodeとかsuccessorなどより説明的な変数名を割り当てても良いと思いました。
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. ありがとうございます。nextだとnextがそのループを抜けるとcurrentになり…とか考えると下手に意味を持たせないほうがいいかなと思いましたが、もっと距離が離れたりするとtmpではわからなくなりそうですね。successorは空間的なニュアンスがありわかりやすいですね。 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. 個人的には |
||
| cur_node->next = reversed_head; | ||
| reversed_head = cur_node; | ||
| cur_node = tmp; | ||
| } | ||
| return reversed_head; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| 再帰別解(https://github.com/maeken4/Arai60/pull/7/files/e30de22cd54e0c4dab8713af400534b11d6adb69#r2118170691) | ||
| ```cpp | ||
| class Solution { | ||
| public: | ||
| ListNode* reverseList(ListNode* head) { | ||
| return reverse_and_append(head, nullptr); | ||
| } | ||
| private: | ||
| // [1,2,3], [4,5,6] -> [3,2,1,4,5,6] | ||
| // [], [4,5,6] -> [4,5,6s] | ||
| ListNode* reverse_and_append(ListNode* head1, ListNode* head2){ | ||
| if (head1 == nullptr) { | ||
| return head2; | ||
| } | ||
| auto new_head1 = head1->next; | ||
| head1->next = head2; | ||
| return reverse_and_append(new_head1, head1); | ||
|
|
||
| } | ||
| }; | ||
| ``` | ||
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.
while のあとにスペースを空けることをお勧めします。
参考までにスタイルガイドへのリンクを貼ります。
https://google.github.io/styleguide/cppguide.html#Horizontal_Whitespace
ただし、上記のスタイルガイドは唯一絶対のルールではなく、複数あるスタイルガイドの一つに過ぎないということを念頭に置くことをお勧めします。また、所属するチームにより何が良いとされているかは変わります。自分の中で良い書き方の基準を持ちつつ、チームの平均的な書き方で書くことをお勧めいたします。
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.
たびたびご指摘頂きすみません。自分の中でルールを決めて気づけるように気をつけます。