LeetCode 725 分割链表
给你一个头结点为 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
均匀分割:
先对链表进行一次扫描,得到总长度cnt
, 再将链表划分成k
分,直接在原链表上进行划分,只需要在合适的分割位置,将节点的next
指针置为空即可
public static ListNode[] splitListToParts(ListNode head, int k) {
// 即使head 为null,也要分割成k个null
if (k == 0) return new ListNode[]{};
// 链表的总长度
int lengths = 0;
ListNode listNode = head;
while (listNode != null) {
lengths++;
listNode = listNode.next;
}
ListNode[] listNodes = new ListNode[k];
// 重新指向链表首节点
ListNode p = head;
// 分割后每部分的最大长度
int partLength = 0;
// 统计已分割部分的总长度
int splitedLength = 0;
for (int i = 0; i < k; i++) {
// 重新计算剩余部分进行分割的长度 = 链表剩余长度 / 可以分割的若干部分
// 链表剩余长度 = (链表总长度-已分割部分的长度)
// 链表剩余长度+ (可分割成多少段-1) 是因为输入被分成了几个连续的部分,并且每部分的长度相差不超过 1 ,前面部分的长度大于等于后面部分的长度
partLength = (lengths - splitedLength + k - i - 1) / (k - i);
// 统计已分割部分的总长度
splitedLength += partLength;
if (p != null) {
// 指向新的连续部分的开始节点,使用这个指针是为了后续进行分割时将一段连续部分的尾指针和其下一个节点进行分割
ListNode q = p;
listNodes[i] = q;
// 需要指向下一个节点,
p = p.next;
// 逐渐遍历节点,使其满足条件的分割成一个连续的部分
for (int j = 0; j < partLength - 1 && p != null; j++) {
q = q.next;
p = p.next;
}
q.next = null;
} else {
break;
}
}
return listNodes;
}
每个部分的链表长度计算方式:剩余未处理部分的链表长度分割成待分配份数,
partLength = (lengths - splitedLength + k - i - 1) / (k - i);
partLength
是当前部分可分配的节点个数;
lengths - splitedLength
是剩余未处理部分的链表长度
k-i-1
是为了保证当前部分的链表长度比后面部分的链表长
k-i
是待分配份数;
也可采取另一种计算方式:
先获取理论上最小分割长度
per=lengths / k;
分配给per长度的节点后,再判断是否还需要再分配一个节点,判断方式是
当「已处理的链表长度 + 剩余待分配份数 * per < cnt」,再分配一个单位长度