题目链接: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
借着这道题,参考着实现了二路归并排序,感觉自己棒棒哒!