目录:
目的
{1,3,2,4,5} 排序变成 {1,2,3,4,5}
思路
这个任务是将无序单链表变成有序表。推荐使用归并算法。可以理解为汉武帝的推恩令政策(分治思想)。将大块封地分成小块封地(分割链表),对小封地进行整顿,确保符合中央标准(分到最小),将整治好的封地“统一”到中央管理中(合并链表)。
分:递归拆分封地
-
检查诸侯的封地:当一个封地已经小到只有一块地时(链表只有一个节点或为空),就无需再分封了。
-
长子拿一半封地:为了分封,需要找到封地的中间点,然后将封地一分为二。
- 左半部分是给长子继承的封地;
- 右半部分是分给其他子孙的封地;right = mid.next(右侧封地的起点)
- 使左侧和右侧完全独立。mid.next = None
治:治理每块小封地
要确保每块封地都封到无可再封的地步,长子和其他子孙的封地还要再封。
left_sorted = self.sortInList(head)
right_sorted = self.sortInList(right)
合:整合治理好的子封地:
-
返回合并后的完整有序链表,这一步相当于将治理成果上报给中央,最终完成帝国范围内的整体治理return self.merge(left_sorted, right_sorted)。
-
合并两个有序链表:参考文章
力扣刷题TOP101:3.BM4 合并两个排序的链表-优快云博客
-
- 递归的本质是将链表逐步缩小,直到每段链表只有一个节点为止(递归终止条件),然后从底向上逐步排序合并。
中央辅助:两位中央监督员
这两位监督员巡视分封的封地,通过协调配合准确找到分封的中点。
- 慢速监督员 slow从head开始 (封地的开端),一次检查一个,确保不遗漏任何细节。
- 快速监督员 fast从head.next开始(封地稍远的位置),一次检查两个,快速推进巡视进度。
- 当快速监督员走到封地末尾时(
fast
为None
或fast.next
为None
),慢速监督员正好站在封地的中点。-
为什么是中点:
- 快速监督员到链表的末尾时,快速监督员移动的总路径就是链表的长度 n。
- 慢速监督员每次移动一步,与快速监督员相比步数正好是其一半。当快速监督员到达末尾时,慢速监督员移动的总路径为:n/2。
-
*fast=head.next
是更安全的初始化方式,它避免了短链表中快指针越界的问题,并确保对奇偶长度链表都能正确找到中点。这种处理方式是归并排序和链表问题中最常用的技巧。
复杂度
-
时间复杂度:O(nlogn)
- 每次切分链表需要 O(logn),合并过程需要 O(n),所以总体为 O(nlogn)。
-
空间复杂度:O(logn)
- 递归调用栈的深度与链表长度的对数成正比。
记忆秘诀
- 分阶段:通过中央指令,逐步分割封地。
- 治阶段:对子封地分别进行整顿。
- 合阶段:最终统一整合封地,确保整个国家井然有序。
python代码
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param head ListNode类 the head node
# @return ListNode类
#
class Solution:
# 工具函数:合并两个有序链表
def merge(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy = ListNode(0) # 虚拟头节点
tail = dummy
# 合并两个有序链表
while l1 and l2:
if l1.val < l2.val:
tail.next = l1
l1 = l1.next
else:
tail.next = l2
l2 = l2.next
tail = tail.next
# 如果l1还有剩余节点
if l1:
tail.next = l1
# 如果l2还有剩余节点
if l2:
tail.next = l2
return dummy.next
# 工具函数:使用快慢指针法找到链表的中点
def getMiddle(self, head: ListNode) -> ListNode:
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
# 归并排序主函数
def sortInList(self, head: ListNode) -> ListNode:
# 递归终止条件
if not head or not head.next:
return head
# 找到链表的中点并将链表断成两部分
mid = self.getMiddle(head)
right = mid.next
mid.next = None # 将链表断开
# 递归对左右两部分排序
left_sorted = self.sortInList(head)
right_sorted = self.sortInList(right)
# 合并已排序的左右链表
return self.merge(left_sorted, right_sorted)
还有一种更简单的方法,适合投机一点的,思路类似这道题:
力扣刷题TOP101:4.BM5 合并k个已排序的链表-优快云博客
class Solution:
def sortInList(self , head ):
p = head
nums = []
#遍历链表,将节点值加入数组
while p:
nums.append(p.val)
p = p.next
p = head
#对数组元素排序
nums.sort()
#遍历数组
for i in range(len(nums)):
#将数组元素依次加入链表
p.val = nums[i]
p = p.next
return head
* 欢迎大家探讨新思路,能够更好的理解和记忆