leetcode
leetcode 51 ~ 100
旋转链表

旋转链表

难度:

标签:

题目描述

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

 

示例 1:

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

示例 2:

输入:head = [0,1,2], k = 4
输出:[2,0,1]

 

提示:

  • 链表中节点的数目在范围 [0, 500]
  • -100 <= Node.val <= 100
  • 0 <= k <= 2 * 109

代码结果

运行时间: 52 ms, 内存: 15 MB


/*
 * Problem: Rotate a linked list to the right by k positions using Java Streams.
 * Note: As Java Streams are typically used for collections, not linked lists, this solution
 * manually collects the list elements and then uses Streams for rotation.
 * Approach:
 * 1. Convert the linked list to a List.
 * 2. Calculate the effective rotations needed (k % length).
 * 3. Use Java Streams to manipulate the list.
 * 4. Reconstruct the linked list from the rotated list.
 */
 
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}
 
public class RotateListStream {
    public ListNode rotateRight(ListNode head, int k) {
        if (head == null || k == 0) return head;
 
        // Convert linked list to list
        List<ListNode> nodes = new ArrayList<>();
        ListNode current = head;
        while (current != null) {
            nodes.add(current);
            current = current.next;
        }
 
        // Calculate effective rotations needed
        int length = nodes.size();
        k %= length;
        if (k == 0) return head;
 
        // Rotate the list using Streams
        List<ListNode> rotated = IntStream.concat(
                IntStream.range(length - k, length),
                IntStream.range(0, length - k))
                .mapToObj(nodes::get)
                .collect(Collectors.toList());
 
        // Reconstruct the linked list from rotated list
        for (int i = 0; i < rotated.size() - 1; i++) {
            rotated.get(i).next = rotated.get(i + 1);
        }
        rotated.get(rotated.size() - 1).next = null;
 
        return rotated.get(0);
    }
}

解释

方法:

该题解的思路是先遍历一遍链表,得到链表的长度length。然后将链表首尾相连成环,再计算出需要移动的实际步数 step = length - k % length - 1。最后再遍历 step 步,将环断开,得到旋转后的链表。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在处理极端情况,如链表长度为0或1时,这种算法是否有特别的处理方式,或者算法本身就能够正确处理?
在这个算法中,如果链表长度为0(即链表为空),在函数的开始就有一个检查 `if head is None`,此时会直接返回 `None`,因此算法能够正确处理链表为空的情况。对于链表长度为1的情况,由于链表首尾相连后仍是同一个节点,并且 `k % length` 为0,因此任何旋转都不会改变链表的结构,算法也能正确处理这种情况。
🦆
如果k的值远大于链表的长度,例如k是链表长度的数倍,这种情况下算法如何确保高效运行,而不是进行无用的循环?
算法通过取模操作 `k % length` 来处理这种情况。无论k的值有多大,`k % length` 的结果总是在0到length-1之间,这意味着算法只会进行必要的旋转步数,从而避免了无用的循环。这保证了算法的效率,即使k的值远大于链表长度。
🦆
为什么在计算实际需要移动的步数时使用`length - k % length - 1`,而不是直接使用`k % length`?
使用 `length - k % length - 1` 的原因是算法实际上是在寻找新的尾节点。`k % length` 给出的是从头部开始的偏移量,但我们需要找到新链表的尾节点位置,然后将其指向 `None` 来断开环。计算 `length - k % length` 给出的是新的尾节点位置,减1则是因为我们需要从头节点开始计算。
🦆
在将链表首尾相连后,该算法如何确保在断开正确的节点,以避免链表的破坏或错误连接?
在算法中,通过精确计算要移动的步数 `step` 并遵循这一步数来移动到正确的节点,然后断开这个节点的下一个连接来确保正确断开环。由于 `step` 是基于链表长度和k的计算得到的,这保证了旋转后的链表首尾相连的正确性和完整性。通过准确遵循计算得到的步数,我们可以确保不会发生链表的破坏或错误连接。

相关问题

轮转数组

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

 

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

 

提示:

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1
  • 0 <= k <= 105

 

进阶:

  • 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
  • 你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

分隔链表

给你一个头结点为 head 的单链表和一个整数 k ,请你设计一个算法将链表分隔为 k 个连续的部分。

每部分的长度应该尽可能的相等:任意两部分的长度差距不能超过 1 。这可能会导致有些部分为 null 。

k 个部分应该按照在链表中出现的顺序排列,并且排在前面的部分的长度应该大于或等于排在后面的长度。

返回一个由上述 k 部分组成的数组。

 

示例 1:

输入:head = [1,2,3], k = 5
输出:[[1],[2],[3],[],[]]
解释:
第一个元素 output[0] 为 output[0].val = 1 ,output[0].next = null 。
最后一个元素 output[4] 为 null ,但它作为 ListNode 的字符串表示是 [] 。

示例 2:

输入:head = [1,2,3,4,5,6,7,8,9,10], k = 3
输出:[[1,2,3,4],[5,6,7],[8,9,10]]
解释:
输入被分成了几个连续的部分,并且每部分的长度相差不超过 1 。前面部分的长度大于等于后面部分的长度。

 

提示:

  • 链表中节点的数目在范围 [0, 1000]
  • 0 <= Node.val <= 1000
  • 1 <= k <= 50