148.排序链表|从下往上的二分归并

在这里插入图片描述
在这里插入图片描述
分析

  • nlogn的时间复杂度:归并排序、堆排等
  • 常数的空间复杂度:递归需要用到栈,不行。那就倒着来,用interval记录需要归并的两子链范围。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        nlen = 0
        ptr = head # 流动指针
        while ptr: # 遍历得链表长度
            nlen += 1
            ptr = ptr.next

        res = ListNode(next=head)

        interval = 1 # 间隔区间
        
        while interval < nlen: # 归并
            print('----interval')
            print(interval)
            pre = res # 从头开始
            ptr = res.next # 流动指针
            while ptr: # 遍历到头
                print('----ptr')
                print(ptr)
                # 1.流动到的第一个归并子链的头指针
                head1 = ptr 

                # 2.计数流动到的第二个归并子链的头指针
                count = 0 # 子链长度,在这个区间中是有序;比如interval=1时就是单个结点是有序链表
                while count<interval and ptr:
                    print('----count1')
                    print(count)
                    count += 1
                    print(count)
                    ptr = ptr.next # 遍历到第二个归并子链的头指针
                if count < interval:
                    break # 说明此时的从第一个归并子链的头指针到最后都是有序,也就不需要归并排序了,直接下一轮。
                head2 = ptr # 到达第二个归并子链的头指针

                # 3.计算第二个归并子链能达到的长度len2 (因为要原地归并,那就只能计数着范围,调整内链的顺序)
                count = 0
                while count<interval and ptr:
                    print('----count2')
                    print(count)
                    count += 1
                    ptr = ptr.next
                # 如果此时是因为count=interval,循环break的话,len2=count=interval;如果是因为ptr=None的话,说明len2=count<=interval;反正len2=count
                len1, len2 = interval, count
                
                # 4.归并
                while len1 and len2:
                    if head1.val < head2.val:
                        pre.next = head1
                        head1 = head1.next
                        len1 -= 1
                    else:
                        pre.next = head2
                        head2 = head2.next
                        len2 -= 1
                    pre = pre.next
                pre.next = head1 if len1 else head2
                
                # 5.将pre移动到归并好的子链的最后一个
                while len1: 
                    pre = pre.next
                    len1 -= 1
                while len2:
                    pre = pre.next
                    len2 -= 1
                # 6.修改最后结点的next指针,指向下一对要归并的子链,否则会造成链表循环的!!
                pre.next = ptr

            interval *= 2 # 下一轮的归并子链的 区间长度

        return res.next
                           

        

上面代码超时,我检查了下,结果只要去掉print()就能过了…

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        nlen = 0
        ptr = head # 流动指针
        while ptr: # 遍历得链表长度
            nlen += 1
            ptr = ptr.next

        res = ListNode(next=head)

        interval = 1 # 间隔区间
        
        while interval < nlen: # 归并
            pre = res # 从头开始
            ptr = res.next # 流动指针
            while ptr: # 遍历到头
                # 1.流动到的第一个归并子链的头指针
                head1 = ptr 

                # 2.计数流动到的第二个归并子链的头指针
                count = 0 # 子链长度,在这个区间中是有序;比如interval=1时就是单个结点是有序链表
                while count<interval and ptr:
                    count += 1
                    ptr = ptr.next # 遍历到第二个归并子链的头指针
                if count < interval:
                    break # 说明此时的从第一个归并子链的头指针到最后都是有序,也就不需要归并排序了,直接下一轮。
                head2 = ptr # 到达第二个归并子链的头指针

                # 3.计算第二个归并子链能达到的长度len2 (因为要原地归并,那就只能计数着范围,调整内链的顺序)
                count = 0
                while count<interval and ptr:
                    count += 1
                    ptr = ptr.next
                # 如果此时是因为count=interval,循环break的话,len2=count=interval;如果是因为ptr=None的话,说明len2=count<=interval;反正len2=count
                len1, len2 = interval, count
                
                # 4.归并
                while len1 and len2:
                    if head1.val < head2.val:
                        pre.next = head1
                        head1 = head1.next
                        len1 -= 1
                    else:
                        pre.next = head2
                        head2 = head2.next
                        len2 -= 1
                    pre = pre.next
                pre.next = head1 if len1 else head2
                
                # 5.将pre移动到归并好的子链的最后一个
                while len1: 
                    pre = pre.next
                    len1 -= 1
                while len2:
                    pre = pre.next
                    len2 -= 1
                # 6.修改最后结点的next指针,指向下一对要归并的子链,否则会造成链表循环的!!
                pre.next = ptr

            interval *= 2 # 下一轮的归并子链的 区间长度

        return res.next
               
        

参考链接:Sort List (归并排序链表)
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值