【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 。前面部分的长度大于等于后面部分的长度。


关键思路

首先,我们要确定输入链表的长度。从链表的头结点开始遍历,记头节点 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:

  1. 链表长度 l = 10
  2. 分割后链表个数 k = 3
  3. 模 m = 10//3 = 3 (分割后每个链表的基础长度 m)
  4. 余数 r = 10%3 = 1 (前 r 个链表的长度应为 m+1)

由此,原链表应该被分为长度分别为 4,3,3 的 3 个链表。同理,当链表长度为 l ,并且链表个数为k时,我们将得到 r 个长度为 m+1 和 k-r 个长度为 m 的部分。

最后,让我们来看看链表的分割。当我们分隔链表时,从链表的头结点开始遍历,记头节点 head 为 curr,对于每个部分,进行如下操作:

  1. 将 curr 作为当前部分的头结点。
  2. 计算当前部分的长度 s (m+1 或 m)。
  3. 向后移动 s 步,将当前节点 cur 作为当前部分的尾结点。
  4. 当 cur 到达当前部分的尾结点时,需要拆分当前节点 cur 和后面一个结点之间的连接关系,在拆分之前需要存储 cur 的后一个结点 cur.next (代码中变量名为tmp)。
  5. 令 cur 的 next 指针指向 None,完成 cur 和 cur.next 的拆分。
  6. 将 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 的赋值机制有了新的理解。(如下图所示)
在这里插入图片描述

  1. 当我们使用 append() 将当前节点 cur(1->2->3)加入列表后,列表中的 cur 依旧为原来的 reference。
  2. cur.next = None 这条语句相当于改变了 cur 的值(1),但并没有改变原来的 reference,所以列表中的 cur 也会随之改变。
  3. 而下一条 cur = tmp 改变了 cur 的 reference,这就导致了每一轮中 cur 相互独立,也就是说每轮对链表进行分割操作后,只影响当前轮 cur 在列表中对应的值。

(如有理解错误,感谢指出!)


链接

https://leetcode-cn.com/problems/split-linked-list-in-parts

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值