题目描述:
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
示例 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
解题思路:
利用快慢指针,找到中间结点,因为fast比slow多走一倍的长度,因此当fast结束时,slow指向中间结点,将链表按照slow分成左右两边,调用mergeSort函数,继续对子链表进行分割,最终指向合并排序操作。
排序链表(优先队列 / 归并排序(再看)☀) - 排序链表 - 力扣(LeetCode) (leetcode-cn.com)
利用归并的思想,递归地将当前链表分为两段,然后 merge。
分两段的方法是使用快慢指针,fast 一次走两步,slow 一次走一步。因为 fast 指针走的遍历的节点数是 slow 指针遍节点数的两倍,所以当 fast 指针遍历到链表末尾时,此时 slow 指针所在位置就是链表的中间位置,这样就将当前链表分成了两段。
merge 时,把两段头部节点值比较,定义一个 p 指针指向较小的节点,且记录第一个节点,然后两段链表从头一步一步向后走,p 也一直向后走,总是指向较小节点,直至其中一个头为 NULL,继续处理剩下的元素,最后返回记录的头即可。
代码实现:
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == NULL) return NULL;//头结点为空,返回null
return mergeSort(head);
}
ListNode *mergeSort(ListNode *head) {
if (head == NULL || head->next == NULL) return head;
//利用快慢指针来找到链表的中点
ListNode *fast = head;
ListNode *slow = head;
//当满足快指针不为空,并且next指针不为空,next next指针不为空,为了保证修改指针过程中没有越界
while (fast != NULL && fast->next != NULL && fast->next->next != NULL) {
fast = fast->next->next;//快指针走两步
slow = slow->next;//慢指针走一步
}
//块是慢的两倍,所以当块在最后的时候,慢在中间,执行中间结点
ListNode *right = mergeSort(slow->next);// 接下来开始进行分链表,递归分
slow->next = NULL;//不要忘记将慢指针的next指针置为null
ListNode *left = mergeSort(head);//分slow左边的链表,递归分。
return merge(left, right);//最后将已经处理好的左右两边进行归并。
}
ListNode *merge(ListNode *left, ListNode *right) {//归并排序的处理,传入的参数是两个已经拍好顺序的两个链表的头指针。
ListNode *dummyHead = new ListNode(0);//创建头指针,new一个新的元素
ListNode *p = dummyHead;//p指针初始化为头指针,p的作用是指向较小的结点
while (left != NULL && right != NULL) {//两个链表都不为空的时候,进入循环
if (left->val <= right->val) {//从各自的头结点进行比较,当满足left的val小于等于right的val时
p->next = left;//将left指向结点连接到p的后面
left = left->next;//更新left指针,向后移动一个位置
} else {
p->next = right;//否则将right指向的结点,连接到p后面,将right后移一个元素
right = right->next;
}
p = p->next;//更新p,将p指向新创建的链表的最后一个元素。
}
//其中一个链表为空,直接将剩余的元素直接连接到p的后面。
if (left != NULL) p->next = left;
if (right != NULL) p->next = right;
return dummyHead->next;//最终饭后头结点。
}
};