返回

力扣第 148 题 - 轻松排序链表,1800 字详解

后端

力扣第 148 题 - 排序链表

问题陈述:

给你链表的头结点 head,请将其按 升序 排列并返回 排序后的链表。

示例 1:

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

输入:head = []
输出:[]

解法一:归并排序

归并排序是一种经典的排序算法,它将链表分成多个子链表,对每个子链表进行排序,然后将排序后的子链表合并为一个排序后的链表。

  1. 分解: 将链表分成两个子链表。如果链表长度为奇数,则将中间节点作为第一个子链表的尾节点,将剩余节点作为第二个子链表。如果链表长度为偶数,则将中间两个节点作为第一个子链表的尾节点,将剩余节点作为第二个子链表。

  2. 征服: 对每个子链表递归应用归并排序。

  3. 合并: 将排序后的子链表合并为一个排序后的链表。可以使用两个指针来合并子链表,一个指针指向第一个子链表的头部,另一个指针指向第二个子链表的头部。将较小的节点添加到合并后的链表中,并更新指针指向下一个节点。

def merge_sort(head):
    if not head or not head.next:
        return head

    # 将链表分成两个子链表
    slow, fast = head, head.next
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

    mid = slow.next
    slow.next = None

    # 递归对子链表进行归并排序
    left = merge_sort(head)
    right = merge_sort(mid)

    # 合并两个排序后的子链表
    return merge(left, right)


def merge(left, right):
    dummy = ListNode()
    curr = dummy

    while left and right:
        if left.val < right.val:
            curr.next = left
            left = left.next
        else:
            curr.next = right
            right = right.next
        curr = curr.next

    curr.next = left or right

    return dummy.next

解法二:快速排序

快速排序是一种经典的排序算法,它选择一个基准节点,将链表分成两个子链表,一个子链表包含所有小于基准节点的节点,另一个子链表包含所有大于基准节点的节点。然后对两个子链表递归应用快速排序,直到所有子链表都排序完成。

  1. 选择基准节点: 选择链表的中间节点作为基准节点。如果链表长度为奇数,则将中间节点作为基准节点。如果链表长度为偶数,则将中间两个节点的平均值作为基准节点。

  2. 分区: 将链表分成两个子链表,一个子链表包含所有小于基准节点的节点,另一个子链表包含所有大于基准节点的节点。可以使用两个指针来分区链表,一个指针指向基准节点,另一个指针指向链表的头部。将较小的节点添加到左子链表中,将较大的节点添加到右子链表中。

  3. 递归: 对两个子链表递归应用快速排序。

def quick_sort(head):
    if not head or not head.next:
        return head

    # 选择基准节点
    pivot = head.val

    # 将链表分成两个子链表
    left, right = ListNode(), ListNode()
    left_curr, right_curr = left, right

    curr = head.next
    while curr:
        if curr.val < pivot:
            left_curr.next = curr
            left_curr = left_curr.next
        else:
            right_curr.next = curr
            right_curr = right_curr.next
        curr = curr.next

    left_curr.next = None
    right_curr.next = None

    # 递归对子链表进行快速排序
    left = quick_sort(left.next)
    right = quick_sort(right.next)

    # 合并两个排序后的子链表
    head.next = left
    curr = head
    while curr.next:
        curr = curr.next
    curr.next = right

    return head

性能分析

归并排序和快速排序都是经典的排序算法,它们的平均时间复杂度都是 O(n log n),其中 n 是链表的长度。然而,快速排序的最好情况时间复杂度是 O(n),最坏情况时间复杂度是 O(n^2),而归并排序的最好情况和最坏情况时间复杂度都是 O(n log n)。

在实践中,快速排序通常比归并排序更快,因为快速排序不需要额外的空间来合并子链表。然而,如果链表已经接近有序,那么归并排序可能比快速排序更快。