LeetCode 热题 HOT 100 第四十八天 148. 排序链表 中等题 用python3求解

题目地址

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

示例 1:
在这里插入图片描述
输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:
在这里插入图片描述
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:
输入:head = []
输出:[]

提示:
链表中节点的数目在范围 [0, 5 * 104] 内
-10的5次方 <= Node.val <= 10的5次方
在这里插入图片描述
在这里插入图片描述
解法:归并排序(从底至顶直接合并)
参考:Sort List (归并排序链表)

对于非递归的归并排序,需要使用迭代的方式替换cut环节:
我们知道,cut环节本质上是通过二分法得到链表最小节点单元,再通过多轮合并得到排序结果。
每一轮合并merge操作针对的单元都有固定长度intv,例如:

  • 第一轮合并时intv = 1,即将整个链表切分为多个长度为1的单元,并按顺序两两排序合并,合并完成的已排序单元长度为2。
  • 第二轮合并时intv = 2,即将整个链表切分为多个长度为2的单元,并按顺序两两排序合并,合并完成已排序单元长度为4。
  • 以此类推,直到单元长度intv >= 链表长度,代表已经排序完成。

根据以上推论,我们可以仅根据intv计算每个单元边界,并完成链表的每轮排序合并,例如:

  • 当intv = 1时,将链表第1和第2节点排序合并,第3和第4节点排序合并,……。
  • 当intv = 2时,将链表第1-2和第3-4节点排序合并,第5-6和第7-8节点排序合并,……。
  • 当intv = 4时,将链表第1-4和第5-8节点排序合并,第9-12和第13-16节点排序合并,……。

此方法时间复杂度O(nlogn),空间复杂度O(1)。
在这里插入图片描述
模拟上述的多轮排序合并:

统计链表长度length,用于通过判断intv < length判定是否完成排序;

额外声明一个节点res,作为头部后面接整个链表,用于:

  • intv *= 2即切换到下一轮合并时,可通过res.next找到链表头部h;
  • 执行排序合并时,需要一个辅助节点作为头部,而res则作为链表头部排序合并时的辅助头部pre;后面的合并排序可以将上次合并排序的尾部tail用做辅助节点。

在每轮intv下的合并流程:
1.根据intv找到合并单元1和单元2的头部h1, h2。由于链表长度可能不是2^n,考虑边界条件:

  • 在找h2过程中,如果链表剩余元素个数少于intv,则无需合并环节,直接break,执行下一轮合并;
  • 若h2存在,但以h2为头部的剩余元素个数少于intv,也执行合并环节,h2单元的长度为c2 = intv - i。

2.合并长度为c1, c2的h1, h2链表,其中:

  • 合并完后,需要修改新的合并单元的尾部pre指针指向下一个合并单元头部h。(在寻找h1, h2环节中,h指针已经被移动到下一个单元头部)
  • 合并单元尾部同时也作为下次合并的辅助头部pre。

3.当h == None,代表此轮intv合并完成,跳出。

每轮合并完成后将单元长度×2,切换到下轮合并:intv *= 2。

代码实现:

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        h= head
        length = 0
        intv = 1
        
        while h:
            h = h.next
            length = length + 1
        
        res = ListNode(0)
        res.next = head
        
        # merge the list in different intv.
        while intv < length:
            pre = res
            h = res.next
            while h:
                # get the two merge head `h1`, `h2`
                h1 = h
                i = intv
                while i and h:
                    h = h.next
                    i = i-1
                if i:
                    break # no need to merge because the `h2` is None.
                h2 = h
                i = intv
                while i and h:
                    h = h.next
                    i = i-1
                c1 = intv
                c2 = intv - i # the `c2`: length of `h2` can be small than the `intv`.
                # merge the `h1` and `h2`.
                while c1 and c2:
                    if h1.val < h2.val:
                        pre.next = h1
                        h1 = h1.next
                        c1 = c1 - 1
                    else:
                        pre.next = h2
                        h2 = h2.next
                        c2 = c2 - 1
                    pre = pre.next
                if c1:
                    pre.next = h1
                else:
                    pre.next = h2
                while c1 > 0 or c2 > 0:
                    pre = pre.next
                    c1 = c1 - 1
                    c2 = c2 - 1
                pre.next = h 
            intv *= 2
        return res.next

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值