【LeetCode 热题 100】148. 排序链表——(解法一)暴力解

Problem: 148. 排序链表
题目:给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

整体思路

这段代码旨在解决一个经典的链表问题:排序链表 (Sort List)。问题要求对一个给定的单向链表进行升序排序。

该实现采用了一种非常直观但不符合题目通常要求(题目通常要求 O(N log N) 时间复杂度和 O(1) 空间复杂度,即原地归并排序)的方法。它将链表问题转化为了数组问题来解决,其核心逻辑步骤如下:

  1. 抽取链表数据到数组(列表)

    • 算法首先创建了一个动态数组 ArrayList<Integer> temp
    • 然后,通过一次遍历,将链表中所有节点的值(cur.val)按顺序抽取出来,并添加到 temp 列表中。
    • 目的:这样做将链表的排序问题“降维”成了一个我们更熟悉的数组排序问题。数组(或列表)提供了比链表更灵活的随机访问和排序能力。
  2. 对数组进行排序

    • 在所有节点值都存入 temp 列表后,算法调用 Collections.sort(temp)
    • Collections.sort() 是 Java 集合框架提供的强大排序工具,它底层对于 ArrayList 使用的是一种经过优化的归并排序(Timsort),能够高效地对列表中的元素进行升序排序。
  3. 将排序后的数据写回链表

    • 现在 temp 列表中存储的是排序好的节点值序列。
    • 算法再次从头遍历原始链表(cur = head)。
    • 在遍历过程中,将 temp 列表中排好序的值,按顺序逐一写回到链表的对应节点中。即 cur.val = temp.get(i++)
    • 这一步只修改了节点的值 (val),而没有改变链表的结构(即节点的 next 指针关系)。
  4. 返回结果

    • 当所有节点的值都被更新为排序后的值之后,原始链表的结构并未改变,但其内容已经有序。直接返回原始的头节点 head 即可。

总结来说,这是一个通过“借用”数组的排序能力来间接完成链表排序的解法。它思路简单,易于实现,但在面试或竞赛中通常不被认为是标准答案,因为它没有在链表结构上直接进行排序操作。

完整代码

/**
 * Definition for singly-linked list.
 */
class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

class Solution {
    /**
     * 对一个单向链表进行升序排序。
     * @param head 链表的头节点
     * @return 排序后链表的头节点
     */
    public ListNode sortList(ListNode head) {
        // 步骤 1: 将链表节点的值抽取到一个动态数组中
        List<Integer> temp = new ArrayList<>();
        ListNode cur = head;
        while (null != cur) {
            temp.add(cur.val);
            cur = cur.next;
        }
        
        // 步骤 2: 使用集合工具类对数组进行排序
        // Collections.sort() 底层使用高效的排序算法(如Timsort)
        Collections.sort(temp);
        
        // 步骤 3: 将排序好的值写回到原始链表的节点中
        cur = head; // 重置指针到链表头部
        int i = 0;  // 数组索引
        while (null != cur) {
            // 更新当前节点的值为排序后数组中的对应值
            cur.val = temp.get(i++);
            cur = cur.next;
        }
        
        // 返回原始链表的头节点,此时其节点值已经有序
        return head;
    }
}

时空复杂度

时间复杂度:O(N log N)

  1. 抽取数据:第一个 while 循环遍历整个链表一次,将 N 个元素添加到 ArrayList 中。这个过程的时间复杂度为 O(N)
  2. 排序Collections.sort(temp) 是算法中时间开销最大的部分。对一个包含 N 个元素的列表进行排序,其时间复杂度为 O(N log N)
  3. 写回数据:第三个 while 循环再次遍历整个链表一次,更新 N 个节点的值。这个过程的时间复杂度为 O(N)

综合分析
算法的总时间复杂度由排序主导:O(N) + O(N log N) + O(N)。因此,最终的时间复杂度是 O(N log N)

空间复杂度:O(N)

  1. 主要存储开销:算法创建了一个 ArrayList<Integer> temp 来存储链表中所有节点的值。
  2. 空间大小:如果链表的长度为 N,那么 temp 列表的大小也将是 N
  3. 其他变量cur, i 等变量只占用常数级别的空间,即 O(1)。
  4. 排序的额外空间Collections.sort() 使用的 Timsort 算法需要额外的空间,最坏情况下为 O(N)。但这与我们已经分配的 temp 列表是同一数量级。

综合分析
算法所需的额外空间主要由 temp 列表决定。因此,其空间复杂度为 O(N)。这不满足题目通常要求的 O(1) 空间复杂度(原地排序)。

【LeetCode 热题 100】148. 排序链表——(解法二)分治

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xumistore

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值