题目
给你一个头结点为 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 。前面部分的长度大于等于后面部分的长度。
关键思路
首先,我们要确定输入链表的长度。从链表的头结点开始遍历,记头节点 head 为 cur;当 cur 向后移动时,记下一个节点 cur.next 为 cur,同时计数。遍历结束后,我们得到链表长度为 l 。
然后,我们需要确定原链表被分割后每个部分的长度。由于任意两部分的长度差距不能超过1,并且前面部分的长度必须大于后面长度,因此我们需要求链表长度 l 和 分割后链表个数 k 的模 m 和余数 r 。
例如,对于 head = [1,2,3,4,5,6,7,8,9,10], k = 3:
- 链表长度 l = 10
- 分割后链表个数 k = 3
- 模 m = 10//3 = 3 (分割后每个链表的基础长度 m)
- 余数 r = 10%3 = 1 (前 r 个链表的长度应为 m+1)
由此,原链表应该被分为长度分别为 4,3,3 的 3 个链表。同理,当链表长度为 l ,并且链表个数为k时,我们将得到 r 个长度为 m+1 和 k-r 个长度为 m 的部分。
最后,让我们来看看链表的分割。当我们分隔链表时,从链表的头结点开始遍历,记头节点 head 为 curr,对于每个部分,进行如下操作:
- 将 curr 作为当前部分的头结点。
- 计算当前部分的长度 s (m+1 或 m)。
- 向后移动 s 步,将当前节点 cur 作为当前部分的尾结点。
- 当 cur 到达当前部分的尾结点时,需要拆分当前节点 cur 和后面一个结点之间的连接关系,在拆分之前需要存储 cur 的后一个结点 cur.next (代码中变量名为tmp)。
- 令 cur 的 next 指针指向 None,完成 cur 和 cur.next 的拆分。
- 将 cur.next 赋值给 cur。
除此之外,我们还需要注意到链表遍历结束,cur 为 None 的情况。在这种情况下,当前部分设置为 cur (也就是 None)即可。
代码实现
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def splitListToParts(self, head, k):
"""
:type head: ListNode
:type k: int
:rtype: List[ListNode]
"""
cur = head
l = 0
while cur!=None:
l += 1
cur = cur.next
m,r = l//k, l%k
result = []
cur = head
for i in range(k):
result.append(cur)
if cur:
s=m+1 if i<r else m
for j in range(s-1):
cur = cur.next
tmp = cur.next # next node of cur is stored
cur.next = None # let next node of cur point to None
cur = tmp # new reference
return result
运行结果
总结反思
一开始我不太能理解,为什么将 cur 存入列表后,cur.next = None 这句使得列表中原本被存储好的 cur 也被分割。(即便程序运行结果是对的。)
这个问题卡了我将近一个下午=.=
后来在寻求小青蛙的帮助后,我对 python 的赋值机制有了新的理解。(如下图所示)
- 当我们使用 append() 将当前节点 cur(1->2->3)加入列表后,列表中的 cur 依旧为原来的 reference。
- cur.next = None 这条语句相当于改变了 cur 的值(1),但并没有改变原来的 reference,所以列表中的 cur 也会随之改变。
- 而下一条 cur = tmp 改变了 cur 的 reference,这就导致了每一轮中 cur 相互独立,也就是说每轮对链表进行分割操作后,只影响当前轮 cur 在列表中对应的值。
(如有理解错误,感谢指出!)