分析
- 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 (归并排序链表)