[148. 排序链表]
「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。
题目描述
给你链表的头结点 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) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
思路
我们已经做了好多道链表相关的题了,今天这里结合双指针,排序算法,递归来看一下这道题。由于链表只能单向访问并且不能随机读取,但是在拼接上十分方便,所以对链表的排序使用归并排序。归并排序是分而治之,递归的思想,其在链表思路是将链表分成两段排序好的链表,再将这两段链表合并起来,这两段排序好的链表也是通过上述思路,进行分割合并得到的,是一个递归的过程。具体的操作有分割和合并。
代码实现
按以上思路,分割需要实现找中间节点,我们之前已经写过。来看下递归的合并函数,递归函数的重要一点就是要明确退出的条件。合并链表时,使用一个哑节点,更方便我们的编码。
class Solution {
private:
ListNode* middleNode(ListNode* head) { //快慢指针找中间节点
ListNode* slow = head;
ListNode* fast = head;
while (fast->next != nullptr && fast->next->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
public:
ListNode* sortList(ListNode* head) {
if(head == NULL || head->next == NULL) //节点为空或单节点不需合并直接退出
return head;
/* 分割链表 */
ListNode* brk=middleNode(head);
ListNode* mid=brk->next;
brk->next = nullptr;
ListNode* head1 = sortList(head);
ListNode* head2 = sortList(mid);
ListNode dummy(0);
ListNode* cur = &dummy;
while(head1 != nullptr && head2 != nullptr){ //合并链表
if(head1->val >= head2->val){
cur->next = head2;
head2 = head2->next;
}else {
cur->next = head1;
head1 = head1->next;
}
cur = cur->next;
}
cur->next=head1==nullptr?head2:head1; //拼接余下部分
return dummy.next;
}
};
总结
在链表上使用双指针,使用归并排序在链表上进行排序。