题目
链表:排序链表
给你链表的头结点 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
进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
代码
/**
* 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) {
if(!head||!head->next)//若该链表为空或者只有一个节点直接返回头结点
{
return head;
}
ListNode* slow=head;//通过快慢指针的方法找到中间节点
ListNode* fast=head->next;
while(fast&&fast->next)//通过循环可以找到中间节点,此时该节点就是拆分后第一个链表的最后一个节点
{
slow=slow->next;
fast=fast->next->next;
}
ListNode* second=slow->next;//第二个链表
slow->next=NULL;//将两个链表分开
ListNode* first=head;//第一个链表
first=sortList(first);//通过递归的方式继续将链表一分位2
second=sortList(second);
return mergeList(first,second);//然后将链表合并,并且返回合并后的链表
}
ListNode* mergeList(ListNode* left,ListNode* right)
{
ListNode* dummy=new ListNode(0);//虚拟节点可以避免处理一些边界条件
ListNode* tail=dummy;//尾节点
while(left&&right)
{
if(left->val<right->val)
{
tail->next=left;
left=left->next;
}else
{
tail->next=right;
right=right->next;
}
tail=tail->next;//尾节点始终指向链表的最后一个节点
}
if(left)//最后若left不为空,直接连接到尾节点后面,否则连接right
{
tail->next=left;
}else{
tail->next=right;
}
return dummy->next;
}
};
原理图
基于链表的特性适合归并排序,时间复杂度O(nlogn),空间复杂度O(1)
假设链表中有n个节点,每次拆分是logn合并也是logn,每个节点都参与了,所以总起来是nlogn。
原理解释
提示:算法流程及解释在代码中已标注
总体步骤需要分为两部分,一部分是将链表通过递归一分为2,第二部分是通过比较大小合并链表
如上原理图流程:
①将4、2、1、3划分为4、2和1、3
②然后将4、2划分为4和2,将1、3划分为1和3,相当于四个链表
③接下来是合并操作,将4、2这两个链表通过比较合并为2、4,1、3这两个链表通过比较合并为2、4
④然后再将合并后的两个链表比较,1比2小,1后边连接2,然后是3相对较小,最后把4连在后边。
小结
排序链表的归并排序算法是一种高效的排序方法,它基于分治思想,将链表逐步拆分成更小的子问题,然后合并这些问题的解决方案以得到最终排序结果。这样的归并排序算法在时间复杂度上为 O(nlogn),空间复杂度为 O(1)。
原理图借鉴哔站华南溜达虎,如有侵权联系删除。