题目

标题和出处

标题:将链表分隔成 K 个部分

出处:725. 将链表分隔成 K 个部分

难度

5 级

题目描述

要求

给你单链表的头结点 链表题目:将链表分隔成 K 个部分 - 图1 和一个整数 链表题目:将链表分隔成 K 个部分 - 图2,将链表分隔成 链表题目:将链表分隔成 K 个部分 - 图3 个连续的部分。

每部分的长度应尽可能相等:任意两部分的长度相差不超过 链表题目:将链表分隔成 K 个部分 - 图4。可能有些部分为 链表题目:将链表分隔成 K 个部分 - 图5

每个部分的顺序应和输入链表的顺序相同,并且前面部分的长度应总是大于或等于后面部分的长度。

返回分隔成的 链表题目:将链表分隔成 K 个部分 - 图6 个部分的数组。

示例

示例 1:

链表题目:将链表分隔成 K 个部分 - 图7

输入:链表题目:将链表分隔成 K 个部分 - 图8
输出:链表题目:将链表分隔成 K 个部分 - 图9
解释:
第一个元素 链表题目:将链表分隔成 K 个部分 - 图10 满足 链表题目:将链表分隔成 K 个部分 - 图11链表题目:将链表分隔成 K 个部分 - 图12
最后一个元素 链表题目:将链表分隔成 K 个部分 - 图13链表题目:将链表分隔成 K 个部分 - 图14,空链表的字符串表示是 链表题目:将链表分隔成 K 个部分 - 图15

示例 2:

链表题目:将链表分隔成 K 个部分 - 图16

输入:链表题目:将链表分隔成 K 个部分 - 图17
输出:链表题目:将链表分隔成 K 个部分 - 图18
解释:
输入链表被分隔成连续的部分,每部分的长度相差不超过 链表题目:将链表分隔成 K 个部分 - 图19,前面部分的长度大于或等于后面部分的长度。

数据范围

  • 链表中结点的数目在范围 链表题目:将链表分隔成 K 个部分 - 图20
  • 链表题目:将链表分隔成 K 个部分 - 图21
  • 链表题目:将链表分隔成 K 个部分 - 图22

解法

思路和算法

由于每个部分的长度和原始链表的长度有关,因此需要遍历原始链表得到原始链表的长度,即结点数。

记原始链表的长度为 链表题目:将链表分隔成 K 个部分 - 图23,令 链表题目:将链表分隔成 K 个部分 - 图24链表题目:将链表分隔成 K 个部分 - 图25,则分隔成的 链表题目:将链表分隔成 K 个部分 - 图26 个部分中,前面 链表题目:将链表分隔成 K 个部分 - 图27 个部分的长度为 链表题目:将链表分隔成 K 个部分 - 图28,其余 链表题目:将链表分隔成 K 个部分 - 图29 个部分的长度为 链表题目:将链表分隔成 K 个部分 - 图30

将原始链表分隔成 链表题目:将链表分隔成 K 个部分 - 图31 个部分的做法是,找到每个部分的头结点和尾结点,将每个部分的头结点存入结果链表,并将每个部分的尾结点和后一个部分的头结点的连接关系断开。

链表题目:将链表分隔成 K 个部分 - 图32 表示当前遍历到的结点,初始时 链表题目:将链表分隔成 K 个部分 - 图33。对于每个部分进行如下操作:

  1. 当前部分的头结点即为 链表题目:将链表分隔成 K 个部分 - 图34,将 链表题目:将链表分隔成 K 个部分 - 图35 存入结果数组的对应下标处;
  2. 计算得到该部分的长度 链表题目:将链表分隔成 K 个部分 - 图36
  3. 链表题目:将链表分隔成 K 个部分 - 图37 向后移动 链表题目:将链表分隔成 K 个部分 - 图38 次,此时 链表题目:将链表分隔成 K 个部分 - 图39 位于该部分的尾结点;
  4. 记录 链表题目:将链表分隔成 K 个部分 - 图40,则 链表题目:将链表分隔成 K 个部分 - 图41 为后一个部分的头结点;
  5. 链表题目:将链表分隔成 K 个部分 - 图42,将当前部分的尾结点和后一个部分的头结点的连接关系断开;
  6. 链表题目:将链表分隔成 K 个部分 - 图43,此时 链表题目:将链表分隔成 K 个部分 - 图44 位于后一个部分的头结点,重复上述操作。

分隔链表的结束条件是 链表题目:将链表分隔成 K 个部分 - 图45 个部分全部分隔完毕,或者链表遍历结束。当链表长度大于或等于 链表题目:将链表分隔成 K 个部分 - 图46 时,每个部分至少有 链表题目:将链表分隔成 K 个部分 - 图47 个结点,因此需要将 链表题目:将链表分隔成 K 个部分 - 图48 个部分全部分隔完毕。当链表长度小于 链表题目:将链表分隔成 K 个部分 - 图49 时,分隔成的 链表题目:将链表分隔成 K 个部分 - 图50 个部分中,前面 链表题目:将链表分隔成 K 个部分 - 图51 个部分的长度都是 链表题目:将链表分隔成 K 个部分 - 图52,其余的 链表题目:将链表分隔成 K 个部分 - 图53 个部分都是空链表,因此当链表遍历结束时即完成分隔。

代码

  1. class Solution {
  2. public ListNode[] splitListToParts(ListNode head, int k) {
  3. int length = 0;
  4. ListNode curr = head;
  5. while (curr != null) {
  6. length++;
  7. curr = curr.next;
  8. }
  9. int quotient = length / k, remainder = length % k;
  10. ListNode[] parts = new ListNode[k];
  11. curr = head;
  12. for (int i = 0; i < k && curr != null; i++) {
  13. parts[i] = curr;
  14. int partLength = quotient + (i < remainder ? 1 : 0);
  15. for (int j = 1; j < partLength; j++) {
  16. curr = curr.next;
  17. }
  18. ListNode next = curr.next;
  19. curr.next = null;
  20. curr = next;
  21. }
  22. return parts;
  23. }
  24. }

复杂度分析

  • 时间复杂度:链表题目:将链表分隔成 K 个部分 - 图54#card=math&code=O%28n%29&id=U6vV0),其中 链表题目:将链表分隔成 K 个部分 - 图55 是链表的长度。需要遍历链表两次,第一次遍历得到链表的长度,第二次遍历分隔链表,分隔链表时对于每个结点的操作的时间都是 链表题目:将链表分隔成 K 个部分 - 图56#card=math&code=O%281%29&id=rEbth)。
  • 空间复杂度:链表题目:将链表分隔成 K 个部分 - 图57#card=math&code=O%281%29&id=W89B4)。除了返回值以外,使用的空间复杂度是常数。