一、介绍
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;
}
};

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

被折叠的 条评论
为什么被折叠?



