【鏈表】旋轉鏈表

一、題目

力扣原題:https://leetcode-cn.com/problems/rotate-list/

二、模擬法

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if (null == head || 0 == k) {
            return head;
        }

	// 計算鏈表長度
        int length = 0;
        ListNode cur = head;
        while (null != cur) {
            length++;
            cur = cur.next;
        }

        // 模擬k輪鏈表旋轉
        for (int i = 0; i < (k % length); i++) {
            ListNode last = head;
            ListNode tmp = head;

            cur = head;
            while (null != cur && null != cur.next) {
                last = cur;
                cur = cur.next;
                tmp = cur;
            }

            if (last != tmp) {
                last.next = null;
                tmp.next = head;
                head = tmp;
            }
        }
        return head;
    }
}
  • 基本思路:根據題目對旋轉鏈表的定義,模擬鏈表旋轉的過程即可。
  • 時間複雜度:遍歷計算鏈表長度O(n),旋轉鏈表的時間複雜度爲O(kn),通過取模運算可以降低外層的循環次數。
    • 最優:O(n)。k % length <= 1時,僅需要進行一輪旋轉即可;
    • 最差:O(n^2)。k % length = n - 1時,需要進行n - 1輪旋轉;
    • 平均:O(n^2)。
  • 空間複雜度:O(1)

二、尋找分割點

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if (null == head || 0 == k) {
            return head;
        }

        // 計算鏈表長度
        int length = 0;
        ListNode cur = head;
        while (null != cur) {
            length++;
            cur = cur.next;
        }

        // 計算偏移量
        int step = length - (k % length);
        if (step == length || step <= 0) {
            return head;
        }

        // 確認分割點
        cur = head;
        for (int i = 0; i < step - 1; i++) {
            cur = cur.next;
        }

        // 斷鏈
        ListNode first = cur.next;
        cur.next = null;

        // 找到第二段鏈表的末尾元素
        cur = first;
        while (null != cur && null != cur.next) {
            cur = cur.next;
        }

        // 重新組鏈
        cur.next = head;
        head = first;
        return head;
    }
}
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if (null == head || 0 == k) {
            return head;
        }

        // 鏈表成環,並計算其長度
        int length = 0;
        ListNode cur = head;
        while (null != cur && null != cur.next) {
            length++;
            cur = cur.next;
        }
        cur.next = head;
        length++;
        
        // 計算步長
        int step = length - (k % length) - 1;

        // 定位分割點
        cur = head;
        for (int i = 0; i < step; i++) {
            cur = cur.next;
        }

        // 斷鏈
        ListNode first = cur.next;
        cur.next = null;
        return first;
    }
}
  • 基本思路:模擬鏈表旋轉,可以發現旋轉相當於將鏈表從某個分割點分割並進行重組。以上展示了兩種不同的思路,可以先分割後重組鏈表,也可以先將鏈表成環後分割,本質上沒有什麼差別。
  • 時間複雜度:O(n)。遍歷計算鏈表長度O(n),斷鏈和組鏈的時間複雜度爲O(n)。
  • 空間複雜度:O(1)。

四、總結

  • 鏈表旋轉具有邏輯上的重複語義,可以通過取模運算降低問題的規模;
  • 可以通過node.next = null斷鏈;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章