61. 旋转链表

61. 旋转链表

✨核心逻辑

本题采用 闭环后断链(先成环,再切断) 的策略:

  1. 边界处理:如果链表为空,或者只有一个节点,直接返回原链表即可。
  2. 计算链表长度并闭环:先遍历整个链表,拿到链表的节点总数 length,同时让尾节点的 next 指向头节点 head,形成一个闭环。
  3. 取模优化:如果右移的步数 k 恰好等于链表长度的整数倍,由于转回原样,直接返回原链表即可。
  4. 寻找新的尾节点:要找到旋转后新的头节点,我们需要先找到新链表的尾节点。经过数学推导,这个新的尾节点正好位于原链表的 length - (k % length) 的位置。
  5. 断开环:找到新的尾节点后,记录它的下一个节点作为待返回的新头节点,然后将新尾节点的 next 置为 null,断开环,返回新头节点。

🔥代码实现(含详细变量注释)

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        // 边界情况判断:如果链表为空,或者只有一个节点,旋转后依旧是原链表,直接返回
        if (head == null || head.next == null) {
            return head;
        }
        
        // copy:辅助遍历指针,初始指向头节点,用于计算链表长度并最终定位到原链表的尾节点
        ListNode copy = head;
        // length:记录链表的总节点数,因为至少有一个节点,所以初始化为 1
        int length = 1;
        
        // 获取链表长度,并将 copy 指针移动到原链表最后一个节点(尾节点)
        while (copy.next != null) {
            copy = copy.next;
            length++;
        }
        
        // 如果向右移动的步数 k 刚好是链表长度的倍数,等于没有移动,直接返回原头节点
        if (k % length == 0) {
            return head;
        }
        
        // sum:计算旋转后,成为新链表尾节点在原链表中的顺位位置(从 1 开始计数)。
        // 例如 length=5, k=2,则新的尾节点是原链表的第 3 个节点。
        int sum = length - k % length;
        
        // 将原链表的尾节点 copy 的 next 指向 head,使链表首尾相连形成闭环
        copy.next = head;
        
        // 从原链表的头节点开始,向后移动 sum - 1 次,找到新的尾节点(此时 head 变为了新尾节点)
        for (int i = 0; i < sum - 1; i++) {
            head = head.next;
        }
        
        // newCopy:记录新尾节点的下一个节点,这个节点也就是旋转后新链表的真实头节点
        ListNode newCopy = head.next;
        
        // 将找到的新尾节点的 next 置为 null,断开整个闭环,完成链表旋转
        head.next = null;
        
        // 返回旋转后新链表的新头节点
        return newCopy;
    }
}
  • ⏱️复杂度分析
    • 时间复杂度:O(N),其中 N 是链表的长度。我们要先遍历一次链表获取长度和连接成环,然后再遍历一次定位新的尾节点并断开,总的时间复杂度为线性 O(N)。

    • 空间复杂度:O(1),仅使用了 copy、length、sum、newCopy 等几个额外的指针和整型变量,没有使用额外的数组空间。

🔍总结一下:

这道题的关键在于成环,就是尾节点指向头节点,进而再找到新链表的头节点,断开定位的尾节点即可