标题:将链表分隔成 K 个部分
出处:725. 将链表分隔成 K 个部分
5 级
给你单链表的头结点 head \texttt{head} head 和一个整数 k \texttt{k} k,将链表分隔成 k \texttt{k} k 个连续的部分。
每部分的长度应尽可能相等:任意两部分的长度相差不超过 1 \texttt{1} 1。可能有些部分为 null \texttt{null} null。
每个部分的顺序应和输入链表的顺序相同,并且前面部分的长度应总是大于或等于后面部分的长度。
返回分隔成的 k \texttt{k} k 个部分的数组。
示例 1:
输入: head = [1,2,3], k = 5 \texttt{head = [1,2,3], k = 5} head = [1,2,3], k = 5
输出: [[1],[2],[3],[],[]] \texttt{[[1],[2],[3],[],[]]} [[1],[2],[3],[],[]]
解释:
第一个元素 output[0] \texttt{output[0]} output[0] 满足 output[0].val = 1 \texttt{output[0].val = 1} output[0].val = 1, output[0].next = null \texttt{output[0].next = null} output[0].next = null。
最后一个元素 output[4] \texttt{output[4]} output[4] 是 null \texttt{null} null,空链表的字符串表示是 [] \texttt{[]} []。
示例 2:
输入: head = [1,2,3,4,5,6,7,8,9,10], k = 3 \texttt{head = [1,2,3,4,5,6,7,8,9,10], k = 3} head = [1,2,3,4,5,6,7,8,9,10], k = 3
输出: [[1,2,3,4],[5,6,7],[8,9,10]] \texttt{[[1,2,3,4],[5,6,7],[8,9,10]]} [[1,2,3,4],[5,6,7],[8,9,10]]
解释:
输入链表被分隔成连续的部分,每部分的长度相差不超过 1 \texttt{1} 1,前面部分的长度大于或等于后面部分的长度。
由于每个部分的长度和原始链表的长度有关,因此需要遍历原始链表得到原始链表的长度,即结点数。
记原始链表的长度为 length \textit{length} length,令 quotient = ⌊ length k ⌋ \textit{quotient} = \Big\lfloor \dfrac{\textit{length}}{k} \Big\rfloor quotient=⌊klength⌋, remainder = length m o d k \textit{remainder} = \textit{length} \bmod k remainder=lengthmodk,则分隔成的 k k k 个部分中,前面 remainder \textit{remainder} remainder 个部分的长度为 quotient + 1 \textit{quotient} + 1 quotient+1,其余 k − remainder k - \textit{remainder} k−remainder 个部分的长度为 quotient \textit{quotient} quotient。
将原始链表分隔成 k k k 个部分的做法是,找到每个部分的头结点和尾结点,将每个部分的头结点存入结果链表,并将每个部分的尾结点和后一个部分的头结点的连接关系断开。
用 curr \textit{curr} curr 表示当前遍历到的结点,初始时 curr = head \textit{curr} = \textit{head} curr=head。对于每个部分进行如下操作:
当前部分的头结点即为 curr \textit{curr} curr,将 curr \textit{curr} curr 存入结果数组的对应下标处;
计算得到该部分的长度 partLength \textit{partLength} partLength;
将 curr \textit{curr} curr 向后移动 partLength − 1 \textit{partLength} - 1 partLength−1 次,此时 curr \textit{curr} curr 位于该部分的尾结点;
记录 next = curr . next \textit{next} = \textit{curr}.\textit{next} next=curr.next,则 next \textit{next} next 为后一个部分的头结点;
令 curr . next : = null \textit{curr}.\textit{next} := \text{null} curr.next:=null,将当前部分的尾结点和后一个部分的头结点的连接关系断开;
令 curr : = next \textit{curr} := \textit{next} curr:=next,此时 curr \textit{curr} curr 位于后一个部分的头结点,重复上述操作。
分隔链表的结束条件是 k k k 个部分全部分隔完毕,或者链表遍历结束。当链表长度大于或等于 k k k 时,每个部分至少有 1 1 1 个结点,因此需要将 k k k 个部分全部分隔完毕。当链表长度小于 k k k 时,分隔成的 k k k 个部分中,前面 length \textit{length} length 个部分的长度都是 1 1 1,其余的 k − length k - \textit{length} k−length 个部分都是空链表,因此当链表遍历结束时即完成分隔。
class Solution {
public ListNode[] splitListToParts(ListNode head, int k) {
int length = 0;
ListNode curr = head;
while (curr != null) {
length++;
curr = curr.next;
}
int quotient = length / k, remainder = length % k;
ListNode[] parts = new ListNode[k];
curr = head;
for (int i = 0; i < k && curr != null; i++) {
parts[i] = curr;
int partLength = quotient + (i < remainder ? 1 : 0);
for (int j = 1; j < partLength; j++) {
curr = curr.next;
}
ListNode next = curr.next;
curr.next = null;
curr = next;
}
return parts;
}
}
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要遍历链表两次,第一次遍历得到链表的长度,第二次遍历分隔链表,分隔链表时对于每个结点的操作的时间都是 O ( 1 ) O(1) O(1)。
空间复杂度: O ( 1 ) O(1) O(1)。除了返回值以外,使用的空间复杂度是常数。