61.リンクリストのローテーション
リンクリストが与えられたら、リンクリストを回転させ、リンクリストの各ノードを右にk 位置移動します 。ここで、 k は非負の数です。
例1:
入力:1-> 2-> 3-> 4-> 5-> NULL、k = 2
出力:4-> 5-> 1-> 2-> 3-> NULL
説明:
右に回転1ステップ:5 -> 1-> 2-> 3-> 4-> NULL
右に回転2ステップ:4-> 5-> 1-> 2-> 3-> NULL
例2:入力:0-> 1-> 2-> NULL、k = 4
出力:2-> 0-> 1-> NULL
説明:
右に回転1ステップ:2-> 0-> 1-> NULL
右に回転2ステップ: 1-> 2-> 0-> NULL
右に回転3ステップ:0-> 1-> 2-> NULL
右に回転4ステップ:2-> 0-> 1-> NULL
アイデア
この種のダブルポインターリンクリストの問題では、いくつかの詳細を扱う場合、2つのポインター間のステップ数の違いという1つの問題しかありません。では、最初のポインターが最初に実行する必要があるステップはいくつですか?
2つの考え方があります。1つは最初のポインタが最後の空でない子ノードで停止する必要があり、停止条件はwhile(first && first-> next)である、もう1つは最初のポインタがで停止する必要があるというものです。リンクリストの空の終了ノードであり、停止条件は「while(first)」です。実際、それは一歩多く、一歩少なくするという問題です。具体的な取り扱いは、各人の習慣や好みに関連しています。
作成者はここに提案をしています。リンクリストの最後の空でない子ノードで最初のポインタを停止することをお勧めします。その理由は次のとおりです。
1:最初のポインタは、空のノードで停止する場合は特に意味はありませんが、チェーンの最後の空でないノードで停止します。とにかく、これはチェーンの最後の有効なノードであることがわかっています。 。この問題はこの情報を効果的に使用します。そうでない場合は、正しいリンクリストのエンドノードを見つけるためにもう一度トラバースする必要があります。
2:空のノードで停止した場合、最初のポインターを計算していくつかの手順を実行すると、実際のパラメーター(たとえば、質問のk)とは異なる場合があり、この値によって、次のことがわからなくなる場合があります。それはk + 1です。それでもk-1、つまりlen-kであり、混乱しがちです。空でない子ノードの末尾で停止する場合、分析は比較的明確であり、間違いを犯しやすいものではありません。
これは推測から外れるものではなく、潜在意識の良い習慣であり、間違いを犯しにくい行動方法です。
解決策:ダブルポインター
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
int cntListNodes(ListNode *head) {
if (NULL == head)
return 0;
int len = 0;
while (head) {
++len;
head = head->next;
}
return len;
}
public:
ListNode* rotateRight(ListNode* head, int k) {
if (NULL == head || NULL == head->next || k < 1)
return head;
int len = cntListNodes(head); //计算长度,求实际移动位置
k %= len;
if (k == 0)
return head;
ListNode *first = head, *second = head; //双指针
while (k--)
first = first->next;
while (first && first->next) { //first要停在链表最后一个非空节点上
first = first->next;
second = second->next;
}
first->next = head; //将链表收尾相连,避免使用临时变量
head = second->next; //head指向新头节点
second->next = NULL; //尾节点置空,防止链表成环
return head;
}
};