148. 排序链表 - 力扣(LeetCode) (leetcode-cn.com)
- 看好多题解都是基于归并排序的。确实链表用归并排序比较适合。且这道题单纯用快速排序在特殊数据超时了,因为链表不支持随机访问,所以不能随机选取主元,导致特殊情况时间复杂度很大。
- 笔者主要利用这道题复习快速排序和递归思想
- 思路基于快速就排序分治思想。创建头结点指向根节点,用根节点作为主元pivot,把比主元小的抽出来插入到主元左边(注意是插入到dummyHead随即后面的下一个(每次都如此)),大的在主元右边不用调整。全部分离后在令pivot的next为NULL(为什么要为NULL?因为链表的结束判断为NULL。没有NULL将无法停止,也就是我们先断开,后面再拼接起来),记得先保存pivot->next哦。然后就分成两段了,分成2个继续递归下去即可。那么怎么重新连接起来呢?先看下面的代码
//类似北航算法课的快排法
//但是由于链表不支持随机访问,所以不能随机选取主元,导致特殊情况时间复杂度很大。leetcode超时了,但是对的
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == NULL || head->next == NULL) return head;
ListNode* dummyHead = new ListNode;
ListNode* pivot = dummyHead->next = head;
ListNode* curPre = head;
ListNode* cur = head->next;
ListNode* tmp1, *tmp2;
while (cur != NULL) {
//如果比pivot小就挪到dummyHead的后一个,注意是随即的后一个哦
if (cur->val < pivot->val) {
tmp1 = dummyHead->next;
tmp2 = cur->next;
dummyHead->next = cur;
cur->next = tmp1;
cur = curPre->next = tmp2;
}
else {
curPre = cur;
cur = cur->next;
}
}
ListNode* head2 = pivot->next;
pivot->next = NULL;//先断尾
//这里是理解递归的关键,每次都先走这里,那么其实是每次递归都先将左边的断开后排序排序
dummyHead->next = sortList(dummyHead->next);
//拼接起来,因为pivot是不会变的,在前面链表的末尾位置不变,即我们让前面链表pivot之前指向空的重新指向一个新排序好的根节点
pivot->next = sortList(head2);
return dummyHead->next;
}
};