LeetCode 148.排序链表

Algorithm

      • [🎯 问题描述](#🎯 问题描述)
      • [🛠️ 思路与方法](#🛠️ 思路与方法)
      • [🧩 归并排序的原理](#🧩 归并排序的原理)
      • [📚 解决方法:归并排序](#📚 解决方法:归并排序)
      • [💻 C++ 实现](#💻 C++ 实现)
      • [🧠 逐步解释](#🧠 逐步解释)
      • [✅ 复杂度分析](#✅ 复杂度分析)
      • [🧩 小结](#🧩 小结)
      • [💡 其他解法(简单介绍)](#💡 其他解法(简单介绍))

🎯 问题描述

给定一个链表的头节点 head,要求将链表按升序排序,并返回排序后的链表。

🛠️ 思路与方法

这个问题本质上是一个链表排序 问题,和常见的数组排序问题类似。常见的排序算法有很多种,最直接的方法是使用 归并排序(Merge Sort) 。因为归并排序具有 O(n log n) 的时间复杂度,而且对于链表来说,归并排序非常适用,它不需要像快速排序那样随机访问数据。

归并排序的优点

  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1),对于链表排序,我们可以使用递归实现归并排序,这样不需要额外的空间开销。

🧩 归并排序的原理

  1. 分割:递归地将链表分成两半,直到每个子链表只有一个节点为止。
  2. 合并:逐层合并这些子链表,使得每个子链表都是排序的。

📚 解决方法:归并排序

  1. 链表分割:我们可以使用快慢指针来找到链表的中点,将链表分为两半。
  2. 归并操作:递归地对两半链表进行排序,然后合并两部分。

💻 C++ 实现

cpp 复制代码
#include <iostream>
using namespace std;

// Definition for singly-linked list.
struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

class Solution {
public:
    // 归并排序主函数
    ListNode* sortList(ListNode* head) {
        if (!head || !head->next) return head;  // 递归终止条件:空链表或只有一个节点
        
        // 步骤1: 分割链表
        ListNode* mid = getMiddle(head);
        ListNode* right = mid->next;
        mid->next = nullptr;  // 将左半部分断开
        
        // 步骤2: 递归排序左半部分和右半部分
        ListNode* left = sortList(head);
        right = sortList(right);
        
        // 步骤3: 合并两个有序链表
        return merge(left, right);
    }
    
private:
    // 获取链表的中间节点
    ListNode* getMiddle(ListNode* head) {
        if (!head) return nullptr;
        ListNode* slow = head;
        ListNode* fast = head;
        
        // 快慢指针:fast 每次移动 2 步,slow 每次移动 1 步
        while (fast->next && fast->next->next) {
            slow = slow->next;
            fast = fast->next->next;
        }
        
        return slow;
    }
    
    // 合并两个有序链表
    ListNode* merge(ListNode* left, ListNode* right) {
        ListNode dummy(0);
        ListNode* curr = &dummy;
        
        // 合并过程
        while (left && right) {
            if (left->val <= right->val) {
                curr->next = left;
                left = left->next;
            } else {
                curr->next = right;
                right = right->next;
            }
            curr = curr->next;
        }
        
        // 连接剩余部分
        if (left) curr->next = left;
        if (right) curr->next = right;
        
        return dummy.next;
    }
};

int main() {
    // 示例:创建一个链表
    ListNode* head = new ListNode(4);
    head->next = new ListNode(2);
    head->next->next = new ListNode(1);
    head->next->next->next = new ListNode(3);

    Solution solution;
    ListNode* sortedList = solution.sortList(head);

    // 打印排序后的链表
    ListNode* p = sortedList;
    while (p) {
        cout << p->val << " ";
        p = p->next;
    }

    return 0;
}

🧠 逐步解释

  1. sortList 函数

    • 递归地分割链表。
    • 如果链表为空或者只有一个节点,则直接返回该链表。
    • 通过调用 getMiddle 找到链表的中间节点,并将链表分成左右两部分。
    • 分别对左半部分和右半部分进行排序。
    • 合并左右两个已经排序的链表,并返回结果。
  2. getMiddle 函数

    • 使用快慢指针来找到链表的中点。slow 每次走一步,fast 每次走两步,直到 fastfast->nextnullptr,此时 slow 就指向链表的中点。
  3. merge 函数

    • 将两个已经排序的链表合并成一个排序好的链表。
    • 使用一个虚拟头节点 dummy 来简化合并过程,最终返回合并后的链表。

✅ 复杂度分析

  • 时间复杂度 :O(n log n),因为每次分割都将链表分成两部分,递归深度为 log n,而合并两个链表的时间是线性的 O(n),因此总的时间复杂度是 O(n log n)
  • 空间复杂度 :O(log n),由于递归调用栈的空间使用,空间复杂度为 O(log n)。如果使用迭代方式来替代递归,空间复杂度可以进一步优化为 O(1)

🧩 小结

  • 归并排序非常适合链表排序,尤其是当链表非常长时,归并排序能够提供一个稳定的 O(n log n) 时间复杂度。
  • 通过链表的分割和合并操作,我们可以非常高效地完成链表的排序。

💡 其他解法(简单介绍)

除了归并排序,还可以使用快速排序或堆排序等方法,但对于链表来说,归并排序是最常见的选择,因为它适合链表的结构,不需要随机访问。

相关推荐
WL_Aurora几秒前
Python 算法基础篇之排序算法(二):希尔、快速、归并
python·算法·排序算法
闻缺陷则喜何志丹6 分钟前
【图论 树 启发式合并】P7165 [COCI2020-2021#1] Papričice|普及+
c++·算法·启发式算法·图论··洛谷
alexwang2117 分钟前
AT_abc458_d [ABC458D] Chalkboard Median题解
c++·算法·题解·atcoder
故事和你918 分钟前
洛谷-【图论2-4】连通性问题1
开发语言·数据结构·c++·算法·动态规划·图论
我先去打把游戏先16 分钟前
Ubuntu虚拟机(服务器版本)Git安装教程(附常用命令)——从零开始掌握版本控制
服务器·c语言·c++·git·嵌入式硬件·物联网·ubuntu
周末也要写八哥17 分钟前
算法实例分析:使数组相等的最小开销
算法
吃好睡好便好21 分钟前
在Matlab中绘制质点运动轨迹图
开发语言·学习·算法·matlab·信息可视化
艾莉丝努力练剑21 分钟前
【Linux网络】Linux 网络编程:HTTP(四)从手写服务器到生产级 Nginx 与 cpp-httplib 实战
linux·运维·服务器·网络·c++·nginx·http
爱炼丹的James24 分钟前
第三章 搜索和图论
数据结构·算法·图论
菜菜笔记25 分钟前
【无标题】
算法