1. 程式人生 > >【LeetCode題解】25_k個一組翻轉連結串列(Reverse-Nodes-in-k-Group)

【LeetCode題解】25_k個一組翻轉連結串列(Reverse-Nodes-in-k-Group)

目錄

更多 LeetCode 題解筆記可以訪問我的 github

描述

給出一個連結串列,每 k 個節點一組進行翻轉,並返回翻轉後的連結串列。

k 是一個正整數,它的值小於或等於連結串列的長度。如果節點總數不是 k 的整數倍,那麼將最後剩餘節點保持原有順序。

示例 :

給定這個連結串列:1->2->3->4->5

k = 2 時,應當返回: 2->1->4->3->5

k = 3 時,應當返回: 3->2->1->4->5

說明 :

  • 你的演算法只能使用常數的額外空間。
  • 你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。

解法一:迭代

思路

要求解這道題,先要構造一個輔助函式,這個函式的作用就是翻轉連結串列(不包括首尾節點)。假設輸入連結串列如下圖所示,其中連結串列表頭為 begin,連結串列尾部為 end

在這裡插入圖片描述

經過函式之後,連結串列的連線變為如下的形式,且將 begin 作為函式的輸出。

在這裡插入圖片描述

具體的實現思路和

LeetCode 第206題翻轉連結串列是一致的,只是函式的輸入和輸出有些不同,這裡不在贅言,直接給出函式的實現。

// Java 版本
private ListNode reverse(ListNode begin, ListNode end) {
    ListNode prev = begin, curr = begin.next;
    ListNode first = curr;
    while (curr != end) {
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    begin.next = prev;
    first.next = curr;
    return first;
}
# Python 版本
def reverse(begin, end):
    prev, curr, first = begin, begin.next, begin.next
    while curr != end:
        curr.next, prev, curr = prev, curr, curr.next
        begin.next, first.next = prev, curr
        return first

有個上面的輔助函式之後,我們就可以利用它求解這道題。假設輸入連結串列為 1 -> 2 -> 3 -> 4 -> 5k=3,首先構造一個虛擬頭節點 dummy,用於統一後面的一系列操作。初始時,設變數 i=0,當 i+1 不能被3整除時,將 head 指標向連結串列的下一個節點移動;當 i+1 能被3整除時,呼叫上面的輔助函式,將 begin 節點和 head.next 節點之間的節點進行翻轉。具體的操作可以看下面的圖片演示。

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

之後,將 head 指標指向 begin 指標的下一個節點(這裡為 4),即 head = begin.next。如此迴圈往復,直到 head 節點為 null,則結束所有操作。

Java 實現

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        // boundary judgement
        boolean hasNoOrOneNode = (head == null || head.next == null);
        if (hasNoOrOneNode || k == 1) {
            return head;
        }

        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode begin = dummy;
        int i = 0;
        while (head != null) {
            ++i;
            if (i % k == 0) {
                begin = reverse(begin, head.next);
                head = begin.next;
            } else {
                head = head.next;
            }
        }
        return dummy.next;
    }

    private ListNode reverse(ListNode begin, ListNode end) {
        ListNode prev = begin, curr = begin.next;
        ListNode first = curr;
        while (curr != end) {
            ListNode next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        begin.next = prev;
        first.next = curr;
        return first;
    }
}
// Runtime: 3 ms
// Your runtime beats 100.00 % of java submissions.

Python 實現

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseKGroup(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        has_no_or_one_node = (not head or not head.next)
        if has_no_or_one_node or k == 1:
            return head
        
        dummy = ListNode(-1)
        dummy.next = head
        begin = dummy
        i = 0
        while head:
            i += 1
            if i % k == 0:
                begin = self._reverse(begin, head.next)
                head = begin.next
            else:
                head = head.next
        return dummy.next
    
    def _reverse(self, begin, end):
        prev, curr, first = begin, begin.next, begin.next
        while curr != end:
            curr.next, prev, curr = prev, curr, curr.next
        begin.next, first.next = prev, curr
        return first

複雜度分析

  • 時間複雜度:\(O(n)\)
  • 空間複雜度:\(O(1)\)

解法二:遞迴(不滿足空間複雜度)

思路

遞迴的思路和迭代的思路是一樣的,也是對 k 個為一組的節點進行翻轉,區別在於遞迴是按照從後往前的順序分別對每組節點進行翻轉,而迭代則是從前往後。

Java 實現

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode curr = head;
        int i = 0;
        while (curr != null && i < k) {
            ++i;
            curr = curr.next;
        }
        
        if (i == k) {
            curr = reverseKGroup(curr, k);
            while (i > 0) {
                ListNode tmp = head.next;
                head.next = curr;
                curr = head;
                head = tmp;
                --i;
            }
            head = curr;
        }
        return head;
    }
}

Python 實現

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseKGroup(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        curr = head
        i = 0
        while curr and i < k:
            curr = curr.next
            i += 1
        
        if i == k:
            curr = self.reverseKGroup(curr, k)
            while i > 0:
                head.next, head, curr = curr, head.next, head
                i -= 1
            head = curr
        return head

複雜度分析

  • 時間複雜度:\(O(n)\)
  • 空間複雜度:\(O(n)\)