【Leetcode】25.K個一組翻轉鏈表

題目描述

25.K個一組翻轉鏈表

給你一個鏈表,每 k 個節點一組進行翻轉,請你返回翻轉後的鏈表。

k是一個正整數,它的值小於或等於鏈表的長度。

如果節點總數不是k的整數倍,那麼請將最後剩餘的節點保持原有順序。

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

示例:
給你這個鏈表:1->2->3->4->5
當k= 2 時,應當返回: 2->1->4->3->5
當k= 3 時,應當返回: 3->2->1->4->5

題目解析

方法一:迭代法

解題思路

這個在上一題兩兩交換鏈表節點的基礎增加了難度,需要K個一組進行翻轉。首先我們可以將鏈表分爲 “已翻轉”、“待翻轉”、“未翻轉” 三部分。在每次進行翻轉前,通過K值確定翻轉鏈表的範圍。

在上面一題已經提到進行鏈表翻轉時需要注意鏈表當前遍歷節點、其前驅節點和後繼節點,需要額外的指針進行標識,防止鏈表翻轉後無法繼續向後遍歷。在這裏我們 prev 指針代表待翻轉鏈表的前驅節點, end 指針代表待翻轉鏈表的末尾, next 指針代表待翻轉鏈表的後繼節點。

代碼示例

Java:

/**
 * Definition for singly-linked list.
 */
public class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { 
        val = x;
    }
}

public ListNode reverseKGroup(ListNode head, int k) {
    if (head == null || head.next == null || k == 0) {
        return head;
    }
    
    // 哨兵節點
    ListNode dummy = new ListNode(0);
    dummy.next = head;
    
    // 待翻轉鏈表的前驅節點
    ListNode prev = dummy;
    // 待翻轉鏈表的結束位置
    ListNode end = dummy;
    
    while(end.next != null) {
        for (int i = 0; i < k && end != null; i++) {
            end = end.next;
        }
        if (end == null) break;

        // 待翻轉鏈表的起始位置
        ListNode start = prev.next;
        // 待翻轉鏈表的後繼節點
        ListNode next = end.next;

        // 將待翻轉鏈表的next指針置爲null,然後翻轉鏈表
        end.next = null;
        prev.next = reverse(start);
        start.next = next;
        
        // 前驅指針和結束指針移動
        prev = start;
        end = prev;
    }
    return dummy.next;
}

// 鏈表反轉
private ListNode reverse(ListNode head) {
    ListNode pre = null;
    ListNode curr = head;
    while (curr != null) {
        ListNode next = curr.next;
        curr.next = pre;
        pre = curr;
        curr = next;
    }
    return pre;
}

複雜度分析

時間複雜度:O(n*k)

空間複雜度:O(1)

方法二:遞歸法

解題思路

遞歸的思路主要在於將子問題傳遞到下一層遞歸函數處理,遞歸函數返回的即正確結果,當前層只需要處理當前層邏輯即可。

當前層只需要處理K個鏈表元素的翻轉,並調用遞歸函數處理後面未翻轉的鏈表,遞歸函數處理完未翻轉鏈表後返回結果,當前層拿到結果後與自身翻轉結束後的鏈表進行拼接,最後得到完整的翻轉結果。

代碼示例

Java:

/**
 * Definition for singly-linked list.
 */
public class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { 
        val = x;
    }
}

public ListNode reverseKGroup(ListNode head, int k) {
    // terminator
    if (head == null || head.next == null || k == 0) {
        return head;
    }
    
    ListNode start = head , end = head;
    for (int i = 0; i < k; i++) {
        if (end == null) return head;
        end = end.next;
    }
    
    // process data: reverse linked list
    ListNode newHead = reverse(start, end);
    
    // recursion
    start.next = reverseKGroup(end, k);
    return newHead;
}

private ListNode reverse(ListNode start, ListNode end) {
    ListNode pre = null, curr = start, next = start;
    while (curr != end) {
        next = curr.next;
        curr.next = pre;
        pre = curr;
        curr = next;
    }
    return pre;
}

複雜度分析

時間複雜度:O(n*k)

空間複雜度:O(n)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章