首先,在排序的题目中,我们先总结、学习一下,链表排序的问题,顺便也回忆一下链表相关操作。对应于下面两道题
147. Insertion Sort List
Sort a linked list using insertion sort.
148. Sort List
Sort a linked list in O(n log n) time using constant space complexity.
链表排序算法解析:
对于链表这种无法靠索引获得值、指针无法逆序(可以递归写?)的数据结构,许多排序算法失效。我们先看基本的几类排序如何:
选择排序,每次遍历链表无序区,找无序区最小值,插入到有序区最后,保存好有序区末尾这个珍贵的指针,实现方法见下一。对于第一题来说,下面勉强可过,原因是有一个测试例子,链表完全倒序,这在选择排序中对应最坏情况。实际上选择和插入平均复杂度是一样的,只是选择排序有这种badcase。
插入排序,最适合链表了,无序区一个指针依次向后取,然后从头开始在有序区查找插入点。代码看下二。注意有一个特殊处理,由于链表无法二分查找,我们先和有序区最后一个比一下,防止最坏的情况~
对于插入排序的优化——希尔排序,需要依靠数组的索引做增量,明显无法实现了;选择排序的优化——堆排序,实际上是在有序区插入时查找更快,也依赖堆的数组实现,不可用。
冒泡排序鄙人没有实现,简单的冒泡就是前后两个节点的元素顺序不对,就调整过来(换节点或换节点值),是可行的。
至于链表的快排,快排的思想是设定一个pivot,在每趟排序中,让pivot左边都小,右边都大;左边依次查找,右边从后向前查找,交换,本质上也是基于交换的。窃以为可以实现,也不敢妄议,如何设计让指针倒着查找即可,有空再研究,而且实现了也不敢说链表上这种快排,时间复杂度还是不是O(nlogn)的。
class Solution:
def selectSortList(self, head: ListNode) -> ListNode:
if not head:
return None
new_h = ListNode(float("-inf"))
new_h.next = head
tail = new_h # 有序区尾
while tail.next:
p = tail.next
min_val = p.val
pre = tail # 最小值前一个节点
temp = tail
# 当前最小
while p:
if p.val < min_val:
pre = temp
min_val = p.val
temp = p
p = p.next
# 最小值删除
pre.next = pre.next.next
# 最小值插入到有序区最后
t = tail.next
tail.next = ListNode(min_val)
tail.next.next = t
tail = tail.next # 有序区加1
return new_h.next
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
if not head:
return None
new_h = ListNode("x")
new_h.next = head
# 有序区尾
tail = new_h.next
tail_val = tail.val
while tail.next:
# 要插入的值
insert_val = tail.next.val
# 找插入位置
if insert_val >= tail_val:
# 位置不变,尾部后移
tail = tail.next
tail_val = tail.val
else:
tail.next = tail.next.next
pos = new_h
while pos.next:
if pos.next.val > insert_val:
break
pos = pos.next
temp = pos.next
pos.next = ListNode(insert_val)
pos.next.next = temp
return new_h.next
对于第二道题,我们来看一下可行的 O(nlogn) 的排序方法。快排刚才质疑了,那么可行的只剩下归并大法了。我们看下面的代码。链表的归并和数组排序的归并,基本思路是一样的。在切分两个归并段时要注意链表尾部=None。
顺便说一下,此题是链表排序的通用方法,并没有对链表元素的数据分布钦定。可惜的是,测试案例中都是正整数?用基数排序的线性排序法也可解决此题。不再列出代码。
class Solution:
def sortList(self, head: ListNode) -> ListNode:
# print(head)
if not head or not head.next:
return head
def merge(h1, h2) -> ListNode:
head = ListNode("x")
p = head
while h1 and h2:
v1 = h1.val
v2 = h2.val
if v1 <= v2:
p.next = h1
h1 = h1.next
else:
p.next = h2
h2 = h2.next
p = p.next
if h1:
p.next = h1
if h2:
p.next = h2
return head.next
pslow = head
pfast = head
while pfast.next and pfast.next.next:
pslow = pslow.next
pfast = pfast.next.next
head_2 = pslow.next
pslow.next = None
merge_left = self.sortList(head)
merge_right = self.sortList(head_2)
return merge(merge_left, merge_right)
熟练掌握链表的归并排序,下面这道题也迎刃而解的。
23. Merge k Sorted Lists
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Example:
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6
题目解析:
将链表的list拆分再拆分,再两个两个归并。性能还不错。
优化方向:可借鉴外排序中介绍的多路归并、败者树等方案。
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if not lists:
return None
if len(lists) == 1:
return lists[0]
def merge_two(h1, h2):
head = ListNode("x")
p = head
while h1 and h2:
val1 = h1.val
val2 = h2.val
if val1 <= val2:
p.next = h1
h1 = h1.next
else:
p.next = h2
h2 = h2.next
p = p.next
if h1:
p.next = h1
if h2:
p.next = h2
return head.next
if len(lists) == 2:
return merge_two(lists[0], lists[1])
mid = len(lists) // 2
merge_l = self.mergeKLists(lists[:mid])
merge_r = self.mergeKLists(lists[mid:])
return merge_two(merge_l, merge_r)