leetcodeHot100|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 = []

输出:[]

提示:

链表中节点的数目在范围 [0, 5 * 104] 内

-105 <= Node.val <= 105

思路分析

首先需要理解题目,题目是给出了一个单链表,需要你去把它排序之后给出来。 核心思路是分而治之,把复杂问题变为小问题解决,再合并解决。

关键是如何去拆链表呢?用快慢指针去找中点,慢指针之走一步,快指针走两步,快指针快到末尾的时候,慢指针就在中点。比如在这个图书中的中点就是5.

如何去合并呢?使用虚拟头节点去凭借两个有序的链表就可以,每次选着一个更小的节点连接上去。

最终演示

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        // 归并排序入口:排序范围是 [head, nullptr)(即从head到链表末尾)
        return sortList(head, nullptr);
    }

    // 递归函数:排序范围是 [head, tail)(左闭右开,tail是终止边界,不包含)
    ListNode* sortList(ListNode* head, ListNode* tail) {
        // 错误1:赋值号= 改成 判断相等==(核心语法错误)
        // 边界条件1:如果当前区间只有0个节点,直接返回null
        if (head == nullptr) {
            return head;
        }
        // 错误2:赋值号= 改成 判断相等==(核心语法错误)
        // 边界条件2:如果当前区间只有1个节点(head->next == tail),断开后续链接并返回
        if (head->next == tail) {
            head->next = nullptr; // 断开与tail的链接,避免后续merge时出现环
            return head;
        }
        // 快慢指针找中间节点(mid):慢指针走1步,快指针走2步
        ListNode* slow = head, *fast = head;
        // 循环条件:快指针未到边界tail
        while (fast != tail) {
            slow = slow->next;       // 慢指针走1步
            fast = fast->next;       // 快指针走1步
            if (fast != tail) {      // 快指针未到边界时,再走1步(总共2步)
                fast = fast->next;
            }
        }
        ListNode* mid = slow; // 找到中间节点,将链表分为 [head, mid) 和 [mid, tail)
        // 递归排序左半部分和右半部分,然后合并
        return merge(sortList(head, mid), sortList(mid, tail));
    } 

    // 合并两个已排序的链表,返回合并后的头节点
    ListNode* merge(ListNode* head1, ListNode* head2) { 
        ListNode* dummyHead = new ListNode(0); // 虚拟头节点(简化链表拼接逻辑)
        ListNode* temp = dummyHead;    // 临时指针,用于构建新链表
        ListNode* temp1 = head1;       // 遍历第一个链表的指针
        ListNode* temp2 = head2;       // 遍历第二个链表的指针
        
        // 循环:同时遍历两个链表,每次取较小值的节点拼接到新链表
        while (temp1 != nullptr && temp2 != nullptr) {  
            if (temp1->val <= temp2->val) { 
                temp->next = temp1;    // 拼接temp1节点
                temp1 = temp1->next;   // temp1后移
            } else { 
                temp->next = temp2;    // 拼接temp2节点
                temp2 = temp2->next;   // temp2后移
            }
            temp = temp->next;         // 临时指针后移,准备拼接下一个节点
        }
        // 处理剩余节点:如果第一个链表还有剩余,直接拼接
        if (temp1 != nullptr) { 
            temp->next = temp1;
        } 
        // 处理剩余节点:如果第二个链表还有剩余,直接拼接(注意这里用else即可,无需else if)
        else {
            temp->next = temp2;
        }
        // 返回合并后链表的真实头节点(跳过虚拟头节点)
        return dummyHead->next;
    }
};

遇到的问题

使用虚拟头结点dummyHead可以不用处理空链表的情况,一定要学会使用,还是就是每次接完一个节点,指针必须往后走,不然容易陷入死循环。最后一个节点也需要注意贴到最后.

总结

链表归并排序的核心是分治,拆分成小链表然后排序再合并其中合并两个有序的链表是基础.

需要项目中图解的可以私信我哦

相关推荐
承渊政道2 小时前
C++学习之旅【unordered_map和unordered_set的使⽤以及哈希表的实现】
c语言·c++·学习·哈希算法·散列表·hash-index
我能坚持多久2 小时前
【初阶数据结构08】——深入理解树与堆
数据结构·算法
Trouvaille ~2 小时前
【贪心算法】专题(一):从局部到全局,数学证明下的最优决策
c++·算法·leetcode·面试·贪心算法·蓝桥杯·竞赛
丶小鱼丶2 小时前
数据结构和算法之【数组】
java·数据结构·算法
承渊政道2 小时前
C++学习之旅【⽤哈希表封装myunordered_map和myunordered_set以及位图和布隆过滤器介绍】
数据结构·c++·学习·哈希算法·散列表·hash-index·图搜索算法
0 0 02 小时前
CCF-CSP 37-4集体锻炼【C++】考点:数学(最大公因数gcd特性),常数优化
开发语言·c++·算法
开源盛世!!2 小时前
3.9-3.11学习笔记
数据结构·算法
天若有情6732 小时前
【C++实用工具】RandEmmet:致敬Emmet的极简随机数生成器(附完整源码+GitHub)
开发语言·c++·github
智者知已应修善业2 小时前
【花费最少钱加油到最后(样例数据推敲)】2024-11-18
c语言·c++·经验分享·笔记·算法