LeetCode 61——旋轉連結串列
1. 題目
2. 解答
2.1. 方法一
將連結串列每個節點向右移動 1 個位置,其實就是讓連結串列最後一個結點指向第一個結點。
因此,向右移動 k 個位置就重複上述過程 k 次即可。
然後,我們注意到,若連結串列有 n 個結點,則移動 n 次後就還是原連結串列。
原始連結串列 1->2->3->NULL
向右旋轉 1 步: 3->1->2->NULL
向右旋轉 2 步: 2->3->1->NULL
向右旋轉 3 步: 1->2->3->NULL
實際上,若連結串列有 n 個結點,我們只需要移動 n % k 次即可。
確定連結串列有多少個結點,我們則可以用快慢指標法。
- 偶數個結點時,結點個數等於 i * 2。
- 奇數個結點時,結點個數等於 i * 2 + 1。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight (ListNode* head, int k) {
if (head == NULL || head->next == NULL) return head;
int node_num = 0; // 連結串列的結點個數
ListNode* slow = head;
ListNode* fast = head;
// 利用快慢指標確定連結串列的結點總個數
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
node_num++;
}
if (fast) // 奇數個結點
{
node_num = node_num * 2 + 1;
}
else // 偶數個結點
{
node_num = node_num * 2;
}
// 若連結串列有 K 個結點,向右移動 K 個位置就還是原連結串列
// 因此我們只需要移動 K % node_num 次即可
if (k % node_num == 0) return head;
else k = k % node_num;
ListNode *temp = head;
ListNode *last_node = NULL;
for (int i = 0; i < k; i++)
{
// 向右移動 1 個位置就是讓最後一個結點指向第一個結點
// 先找到倒數第二個結點
while (temp->next->next)
{
temp = temp->next;
}
// 最後一個結點指向第一個結點,倒數第二個節點指向 NULL
last_node = temp->next;
temp->next = NULL;
last_node->next = head;
head = last_node;
temp = head;
}
return head;
}
};
2.2. 方法二
上面的演算法中每次將連結串列結點向右移動 1 個位置的時候,我們都要遍歷一次連結串列,效率較低。
針對連結串列 1->2->3->4->5->NULL,如果要將連結串列每個結點向右移動 2 個位置,那倒數第 2 個結點就是旋轉後新連結串列的起始位置;如果要將連結串列每個結點向右移動 3 個位置,那倒數第 3 個結點就是旋轉後新連結串列的起始位置;而如果要將連結串列每個結點向右移動 33 個位置,那倒數第 3 個結點就是旋轉後新連結串列的起始位置。
更一般的情況下,若連結串列長度為 n,移動次數為 k,旋轉後連結串列的起始位置就是原連結串列倒數第 k % n 個結點。
在上面的例子中,假設新連結串列的起始位置是結點 4,那連結串列就被分成了兩部分。1->2->3 和 4->5->NULL,我們要做的就是把 1->2->3 拼接在 4->5->NULL 後即可。
實現思路是這樣的:
我們先通過快慢指標確定連結串列長度 n,然後找到旋轉後連結串列起始位置的上一個結點,記為 new_head_last,然後從起始位置 new_head 開始向後遍歷,當到達 NULL 時,將 head 指向的頭結點拼接在 new_head 後面,然後再將新連結串列的最後一個結點也即 new_head_last 後面置為 NULL 即可。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if (head == NULL || head->next == NULL) return head;
int node_num = 0; // 連結串列的結點個數
int mid_num = 0;
ListNode* slow = head;
ListNode* fast = head;
// 利用快慢指標確定連結串列的結點總個數
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
mid_num++;
}
if (fast) // 奇數個結點
{
node_num = mid_num * 2 + 1;
}
else // 偶數個結點
{
node_num = mid_num * 2;
}
// 若連結串列有 K 個結點,向右移動 K 個位置就還是原連結串列
// 因此我們只需要移動 K % node_num 次即可
if (k % node_num == 0) return head;
else k = k % node_num;
int which_node_is_new = 0; // 旋轉後的頭結點是第幾個節點
ListNode* new_head = NULL;
ListNode* new_head_last = NULL;
// 查詢旋轉後頭結點的上一個結點
// 此時 slow 指標指向中間結點,分為在 slow 前還是在 slow 後
which_node_is_new = node_num - k;
if (which_node_is_new > node_num / 2)
{
new_head_last = slow;
for (int i = 0; i < which_node_is_new - mid_num - 1; i++)
{
new_head_last = new_head_last->next;
}
}
else
{
new_head_last = head;
for (int i = 1; i < which_node_is_new; i++)
{
new_head_last = new_head_last->next;
}
}
new_head = new_head_last->next;
ListNode* temp = new_head;
while (temp->next)
{
temp = temp->next;
}
temp->next = head;
new_head_last->next = NULL;
return new_head;
}
};
獲取更多精彩,請關注「seniusen」!