LeetCode 86. 分隔链表

86. 分隔链表

题目:给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
链接 https://leetcode.cn/problems/partition-list/

个人思路
  1. 双指针:
    (1)使用left记录当前走过的最后一个小于 x 的节点,使用right记录遇到的即将走过的小于 x 的节点的前一个节点。如对于[ 0, 1, 7, 3, 5, 2, 8, 9],当left在1的位置时,right走到7的位置,接下来使用temp记录值为3的节点,当right.next.val < x开始交换:
temp = right.next
# 将right.next接到right.next的下一个节点
right.next = right.next.next
# temp的下一个节点指向left的下一个节点
# print(right.val,left.val)
temp.next = left.next
left.next = temp
left = left.next 

交换结束后即为[ 0, 1, 3, 7, 5, 2, 8, 9],此时left为3,left.next还是7,right还是7。
(2)当right.val >= x时,移动right即可right = right.next
(3)但上面那个双指针当开始时会出现left和right在同一个位置的情况,这时候当right.next.val < x进行交换时会出现问题,
我们来具体分析一下每一步得到的结果:首先设置一个dummy(-1,head)虚拟结点指向head,方便后面返回头结点。这时候,left和right都在dummy(因为我们需要上一个结点才能进行指向,即如果right=dummy.next=head时,如果right.val < x时,由于我们不知道上一个节点,我们根本无法进行有效的移动),但为了有效区分left和right,不让二者指向一个dummy,否则会该链会因为right.next = right.next.next和left = left.next 一次跳两步的原因越来越短

dummy = ListNode(-1,head)
left = dummy
right = ListNode(-1,head)
temp = right.next  # 得到temp = 0
# 将right.next接到right.next的下一个节点
right.next = right.next.next   # 得到[-1,1, 7, 3, 5, 2, 8, 9]
# temp的下一个节点指向left的下一个节点
temp.next = left.next  # 此时left为dummy,因为left和right不是同一个,所以left.next.val=0,对temp及其后面得到[0, 0, 1, 7, 3, 5, 2, 8, 9]
left.next = temp # 此时left和right均为dummy,left.next.val=0,对head及其后面得到[0, 0, 1, 7, 3, 5, 2, 8, 9]
left = left.next 
  1. 好吧,上面解释的不是很清楚,但解决上面的问题只需加一个判断left == right即可,如果二者一样,则二者一起向右移动即可。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
        if not head or not head.next:
            return head
# 想过双虚拟结点来解决问题
#         dummy2 = ListNode(0,head)
#         dummy1 = ListNode(0,dummy2)
#         left = dummy1
#         right = dummy2
        dummy = ListNode(-1,head)
        left = dummy
        right = dummy
        while right.next:
            if right.next.val < x:
                if left == right:
                    left = left.next
                    right = right.next
                else:
                    # temp为小于 x 的节点
                    temp = right.next.val
                    temp2 = ListNode(temp)
                    # 将right.next接到right.next的下一个节点
                    right.next = right.next.next
                    # temp的下一个节点指向left的下一个节点
                    # print(right.val,left.val)
                    temp2.next = left.next
                    left.next = temp2
                    left = left.next   
            else:         
                right = right.next
            # 测试每一步是否移动正确
            #   print(right.val,left.val)
            # la = dummy.next
            # ans = []
            # while la:
            #     ans.append(la.val)
            #     la = la.next
            # print(ans)
                
        return dummy.next
  1. 简化后如下:
class Solution:
    def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
        if not head or not head.next:
            return head
        dummy = ListNode(-1,head)
        left = dummy
        right = dummy
        while right.next:
            if right.next.val < x:
                if left == right:
                    left = left.next
                    right = right.next
                else:
                    # temp为小于 x 的节点
                    temp = right.next
                    # 将right.next接到right.next的下一个节点
                    right.next = right.next.next
                    # temp的下一个节点指向left的下一个节点
                    temp.next = left.next
                    left.next = temp
                    left = left.next   
            else:         
                right = right.next
        return dummy.next

复杂度分析
时间复杂度: O(n),其中 n 是原链表的长度。我们对该链表进行了一次遍历。
空间复杂度: O(1)

官方思路

我擦,我好蠢,原来可以将小的放一个链表,大的放另一个链表,然后合起来就行。

  1. 模拟
    (1)直观来说我们只需维护两个链表small 和 large 即可,small 链表按顺序存储所有小于 x 的节点,large 链表按顺序存储所有大于等于 x 的节点。遍历完原链表后,我们只要将 small 链表尾节点指向large 链表的头节点即能完成对链表的分隔。
    (2)为了实现上述思路,我们设 smallHead 和 largeHead 分别为两个链表的哑节点,即它们的 next 指针指向链表的头节点,这样做的目的是为了更方便地处理头节点为空的边界条件。同时设 small 和 large 节点指向当前链表的末尾节点。开始时 smallHead=small,largeHead=large。随后,从前往后遍历链表,判断当前链表的节点值是否小于 x,如果小于就将 small 的 next 指针指向该节点,否则将 large 的 next 指针指向该节点。
    (3)遍历结束后,我们将 large 的 next 指针置空,这是因为当前节点复用的是原链表的节点,而其 next 指针可能指向一个小于 x 的节点,我们需要切断这个引用。同时将 small 的 next 指针指向 largeHead 的 next 指针指向的节点,即真正意义上的 large 链表的头节点。最后返回 smallHead 的 next 指针即为我们要求的答案。
class Solution:
    def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
        if not head or not head.next:
            return head
        small = ListNode(-1,head)
        smallhead = small
        large = ListNode(-1,head)
        largehead = large
        while head:
            if head.val < x:
                small.next = head
                small = small.next
            else:         
                large.next = head
                large = large.next
            head = head.next
        large.next = None
        small.next = largehead.next
        return smallhead.next

参考:
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/partition-list/solution/fen-ge-lian-biao-by-leetcode-solution-7ade/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值