Sort List(二路归并排序)

题目链接:https://leetcode.com/problems/sort-list/description/
题目要求:对链表进行排序,时间复杂度O(n),常数的空间复杂度
两种解法,本质上都是二路归并排序,一个是递归写法,另一个是非递归写法。递归写法简单,工整;非递归写法不太容易理解,也不简洁。
二路归并排序的过程如下所示:
待排序序列为:49,38,65,97,76,13,27。共7个元素
1、将原始序列看成是7个只含有一个元素的子序列,那么这7个序列都是有序的,即如下所示:
[49],[38],[65],[97],[76],[13],[27]
2、两两进行归并。即[49]和[38]进行归并,[65]和[97]进行归并,依次类推,得到如下序列
[38,49],[65,97],[13,76],[27]
27没归并的对象,保持原样即可
3、继续两两进行归并,得到如下两个子序列:
[38,49,65,97],[13,27,76]
4、最后将这两个子序列合为一个序列即可,如下所示:
[13,27,38,49,65,76,97]

以上就是二路归并排序的过程了。那么接下来就是算法的实现过程了。

递归写法如下,挺简单的,重在理解。递归写法是一边断开链接,一边重新链接的过程。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        while head==None or head.next==None:#节点为空或者只有一个节点
            return head#则直接返回
        now,slow,fast=None,head,head#设置快,慢指针
        while fast and fast.next:#fast到达最后,而此时slow到达中间节点
            now=slow
            slow=slow.next
            fast=fast.next.next
        now.next=None#从中间断开链表,这样就形成两段链表
        l1=self.sortList(head)#递归处理左段链表,以使左段的链表有序
        l2=self.sortList(slow)#递归处理右段链表,以使右段的链表有序
        return self.merge(l1,l2)#合并链表并返回
    def merge(self,l1,l2):#对两个有序的链表进行合并
        head=ListNode(0)
        tail=head
        while l1 and l2:
            if l1.val>l2.val:
                tail.next=l2
                l2=l2.next
            else:#l1.val<=l2.val
                tail.next=l1
                l1=l1.next
            tail=tail.next
        if l1:
            tail.next=l1
        if l2:
            tail.next=l2
        return head.next

非递归写法如下,重在理解变量a,b的作用。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#二路归并排序,非递归写法
class Solution(object):
    def getLength(self,head):#得到链表的长度
        length,p=0,head
        while p:
            length+=1
            p=p.next
        return length
    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        block_size,length=1,self.getLength(head)
        i,a,b,iteral=0,0,0,0
        virtual_head=ListNode(0)#辅助节点
        last,it,A,B,temp=None,None,None,None,None
        virtual_head.next=head
        while block_size<length:#需要排序的log(length)趟
            iteral=0#iteral已经排好序的序列中的元素个数
            last,it=virtual_head,virtual_head.next
            while iteral<length:#排好序的序列中元素小于总长度
                #iteral为已经排序的节点个数,所以n-iteral为还剩余多少节点未排序,n-iteral和block取较小值
                #取block_size的时候,为待排序的元素个数大于block_size(length-iteral)
                #当a=length-iteral的时候,那么此时b的值一定为0
                a=min(length-iteral,block_size)
                #b<=a,等于a的时候为还有剩余节点;小于a的时候为没有剩余节点不足block_size
                b=min(length-iteral-a,block_size)
                A=it
                if b!=0:#分别找到待排序的两个子序列。b=0的情况为待排序的第二个子序列的长度为0
                    for i in range(a-1):#找到第一个子序列,子序列的头节点为A
                        it=it.next
                    B=it.next#断开链接的过程
                    it.next=None
                    it=B
                    for i in range(b-1):#找到第二个子序列,子序列的头节点为B
                        it=it.next
                    temp=it.next#断开和后边节点的联系
                    it.next=None
                    it=temp
                while A or B:#然后对两个子序列进行排序
                    if B==None or (A!=None and  A.val<=B.val):
                        last.next=A
                        A=A.next
                        last=last.next
                    else:
                        last.next=B
                        B=B.next
                        last=last.next
                last.next=None#断开的链接,下一个节点的位置已经存储到了it中
                iteral+=a+b#更新访问过的元素个数
            block_size<<=1
        return virtual_head.next

借着这道题,参考着实现了二路归并排序,感觉自己棒棒哒!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值