LeeCode_148. 排序链表(自顶向下递归归并+自顶向上归并)

这篇博客介绍了如何使用归并排序算法对链表进行排序,包括自顶向下递归和自底向上归并两种方法。递归方法虽然直观,但存在额外的空间复杂度。自底向上归并通过逐步合并小的链表段来减少空间需求,最终达到O(n log n)的时间复杂度和常数级空间复杂度。

 一、介绍

1.题目描述

题目链接:https://leetcode-cn.com/problems/sort-list/

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

进阶:

你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

2.测试样例

[4,2,1,3]  # [1,2,3,4]

[-1,5,3,4,0] # [-1,0,3,4,5]

[] # []

二、题解

1、自顶向下递归+归并🔴

1、利用快慢指针找到链表的中间节点

2、利用递归将链表不断进行左右划分

3、对划分的链表两两归并

参考链接:https://leetcode-cn.com/problems/sort-list/solution/pai-xu-lian-biao-by-leetcode-solution/

但这里存在一个递归栈的空间复杂度,因此无法达到题目要求的 O(1)。

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        return recur(head,nullptr);        
    }
    // 递归
    ListNode* recur(ListNode* head,ListNode *tail){
        // 当无元素或只有一个元素,返回
        if(head==nullptr) return head;
        // 将该区段末尾设为null
        if(head->next==tail){
            head->next=nullptr;
            return head;
        }
        // 快慢指针
        ListNode *l=head,*r=head;
        while(r!=tail){
            l=l->next;
            r=r->next;
            if(r!=tail) r=r->next;
        }
        // 中间节点
        ListNode *mid=l;
        // 归并
        return merge(recur(head,mid),recur(mid,tail));
    }
    // 归并
    ListNode* merge(ListNode* l,ListNode* r){
        ListNode* ans=new ListNode(0);
        ListNode* t=ans;
        while(l!=nullptr&&r!=nullptr){
            if(l->val<=r->val){
                t->next=l;
                l=l->next;
            }
            else{
                t->next=r;
                r=r->next;
            }
            t=t->next;
        }
        // 剩余的节点接到后面
        // t->next=l==nullptr?r:l;
        if(l!=nullptr) t->next=l;
        if(r!=nullptr) t->next=r;
        return ans->next;
    }
};

2、自底向上归并

1、求链表长度 n

2、定义 k=1,将原链表分为一个个长度为 k 的链表,两两归并,全部完成后 k*2;

3、直到 k > n,停止

前置题目:LeeCode_21. 合并两个有序链表(新链表+递归)

参考链接:https://leetcode-cn.com/problems/sort-list/solution/pai-xu-lian-biao-by-leetcode-solution/

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        // 统计节点个数
        ListNode* t=head;
        int n=0;
        while(t!=nullptr){
            n++;t=t->next;
        }
        if(n<=1) return head;
        ListNode* ans=new ListNode(0,head);
        // k 代表待归并链表的长度,初始为1,翻倍增长
        for(int k=1;k<n;k*=2){
            // pre更新每组合并后的链表
            ListNode* pre=ans,*cur=ans->next;
            // p,q代表每组待合并两个链表的头
            ListNode* p,*q;
            while(cur!=nullptr){
                // 第一个链表头p
                p=cur;
                for(int i=0;i<k-1&&cur->next!=nullptr;i++) cur=cur->next;
                // 第二个链表头q
                q=cur->next;
                // 第一个链表尾置空
                cur->next=nullptr;
                cur=q;
                for(int i=0;i<k-1&&cur!=nullptr&&cur->next!=nullptr;i++) cur=cur->next;
                // 是否到末尾,没到 cur 指向下一组的头
                ListNode* next=nullptr;
                if(cur!=nullptr){
                    next=cur->next;
                    cur->next=nullptr;
                }
                // pre 更新归并后链表
                pre->next=merge(p,q);
                // pre 后移
                while(pre->next!=nullptr) pre=pre->next;
                cur=next;
            }
        }
        return ans->next;
    }
    
    // 链表归并
    ListNode* merge(ListNode *p,ListNode *q){
        ListNode* ans=new ListNode(0);
        ListNode* t=ans;
        while(p!=nullptr&&q!=nullptr){
            if(p->val<q->val){
                t->next=p;
                p=p->next;
            }
            else{
                t->next=q;
                q=q->next;
            }
            t=t->next;
        }
        t->next=p==nullptr?q:p;
        return ans->next;
    }
};

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值