原题目链接
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
直接实现
使用STL的快排实现,仅供参考
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(!head)
return head;
vector<int> tmpArray; // 定义暂存容器
ListNode *p = head;
while(p)
{
tmpArray.push_back(p -> val);
p = p -> next;
}
sort(tmpArray.begin(), tmpArray.end()); // STL排序
p = head;
for(int i = 0; i < tmpArray.size(); i++)
{
p -> val = tmpArray[i];
p = p -> next;
}
return head;
}
};
复杂度分析:
- 时间复杂度:O(NlogN) 排序时的复杂度
- 空间复杂度:O(N)
题目分析
很多排序算法都可以实现O(NlogN)的时间复杂度:基于二分思想的快排、归并、希尔排序、直接排序、堆排序 都满足。
然而,有需要同时满足空间复杂度为O(1)的排序算法是…希尔排序,归并排序,堆排序
相比之下,归并排序是最常见的,所以就以它作为基础。
解题分析
归并排序三部曲:
- fast-slow找中点:直到快指针移至末尾,慢指针指向的位置就是中间位置
- 将链表分成两部分
- 合并两个有序链表
代码实现
class Solution {
public:
ListNode* sortList(ListNode* head) {
return mergesort(head);
}
ListNode* mergesort(ListNode* node)
{
if(!node || !node -> next)
return node;
ListNode *fast = node;
ListNode *slow = node;
ListNode *jmp = node;
while(fast && fast -> next)
{
fast = fast -> next -> next;
jmp = slow;
slow = slow -> next;
}
jmp -> next = nullptr;
ListNode *l1 = mergesort(node);
ListNode *l2 = mergesort(slow);
return merge(l1, l2);
}
ListNode *merge(ListNode* l1,ListNode* l2)
{
if(l1 == NULL)
return l2;
if(l2 == NULL)
return l1;
if(l1 -> val < l2 -> val)
{
l1 -> next = merge(l1 -> next, l2);
return l1;
}
else
{
l2 -> next = merge(l2 -> next, l1);
return l2;
}
}
};
看到运行结果,内存消耗还是很大,看起来还像是O(N)
复杂度分析:
- 时间复杂度:O(NlogN)
- 空间复杂度:O(N)
返回检查代码,发现在递归调用的过程中用到的空间复杂度不是O(1),而是O(N)?……
注:这里留个坑,没有分析清除。
更正
归并排序其实有两种方式:
- 比较常见的
上面用到的自顶向下 - 自下而上
class Solution {
public:
ListNode* sortList(ListNode* head) {
ListNode dummyHead(0);
dummyHead.next = head;
auto p = head;
int length = 0;
while (p) {
++length;
p = p->next;
}
for (int size = 1; size < length; size <<= 1) {
auto cur = dummyHead.next;
auto tail = &dummyHead;
while (cur) {
auto left = cur;
auto right = cut(left, size); // left->@->@ right->@->@->@...
cur = cut(right, size); // left->@->@ right->@->@ cur->@->...
tail->next = merge(left, right);
while (tail->next) {
tail = tail->next;
}
}
}
return dummyHead.next;
}
ListNode* cut(ListNode* head, int n) {
auto p = head;
while (--n && p) {
p = p->next;
}
if (!p) return nullptr;
auto next = p->next;
p->next = nullptr;
return next;
}
ListNode* merge(ListNode* l1, ListNode* l2) {
ListNode dummyHead(0);
auto p = &dummyHead;
while (l1 && l2) {
if (l1->val < l2->val) {
p->next = l1;
p = l1;
l1 = l1->next;
} else {
p->next = l2;
p = l2;
l2 = l2->next;
}
}
p->next = l1 ? l1 : l2;
return dummyHead.next;
}
};
复杂度分析:
- 时间复杂度:O(NlogN)
- 空间复杂度:O(1)
写在最后
留的坑早点填