程序员面试金典 - 面试题 02.04. 分割链表

题目难度: 中等

原题链接

今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

编写程序以 x 为基准分割链表,使得所有小于 x 的节点排在大于或等于 x 的节点之前。如果链表中包含 x,x 只需出现在小于 x 的元素之后(如下所示)。分割元素 x 只需处于“右半部分”即可,其不需要被置于左右两部分之间。

示例:

  • 输入: head = 3->5->8->5->10->2->1, x = 5
  • 输出: 3->1->2->10->5->5->8

题目思考

  1. 如何做到只使用常数空间?

解决方案

思路
  • 分析题目, 可以发现它很类似快速排序的分割左右两部分的过程
  • 所以我们可以定义两个哨兵, 作为左右两部分开头的前一个节点, 然后依次遍历整个链表, 根据当前节点值与 x 的关系, 断开其与现有 next 节点的连接, 然后将其放在左边或右边, 最终将左边结尾与右边开头相连即可
  • 使用哨兵的目的是统一处理某部分为空的情况, 比如所有节点值都小于 x 时, 此时右半部分是空, 但仍然可以通过左边结尾与右边哨兵的 next 相连, 这就和右半部分不是空的情况使用了相同的处理, 不需要额外判断了
  • 另外特别注意, 在移动节点时一定要断开其与原有 next 节点的连接, 否则假如右半部分的最后一个节点不是原来整个链表的最后一个节点, 且没有断开 next, 此时它会是新链表的结尾, 但其 next 仍指向原始 next 节点, 这样就形成了环
复杂度
  • 时间复杂度 O(N): 只需要遍历整个链表一遍
  • 空间复杂度 O(1): 只使用了几个节点变量
代码
class Solution:
    def partition(self, head: ListNode, x: int) -> ListNode:
        # 双指针, 使用两个哨兵, 分别存储左半部分和右半部分开头的前一个节点
        # 然后遍历整个链表, 将每个节点同x比较, 小的放在左半部分, 大于等于的放在右半部分, 并更新当前遍历节点和对应的左/右节点
        # 循环结束时:
        #   left就是左半部分的最后一个节点, 将其与右半部分开头(即rightdummy.next)相连即可
        #   而right是右半部分最后一个节点, 且其next是空 (因为移动原来节点时已经断开了它与next的连接)
        leftdummy, rightdummy = ListNode(0), ListNode(0)
        left, right = leftdummy, rightdummy
        cur = head
        while cur:
            nex = cur.next
            # 断开当前节点与其现在next节点的连接
            cur.next = None
            if cur.val < x:
                # 将当前节点加入左半部分, 同时更新左半部分结尾
                left.next = cur
                left = cur
            else:
                # 将当前节点加入右半部分, 同时更新右半部分结尾
                right.next = cur
                right = cur
            # 更新当前节点为原链表的next节点
            cur = nex
        # 此时left就是左半部分的结尾, 将其与右半部分开头相连即可
        left.next = rightdummy.next
        return leftdummy.next

大家可以在下面这些地方找到我~😊

我的知乎专栏

我的头条号

我的 CSDN

我的 Leetcode

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值