Sort List
- 问题描述:尝试用O(nlgn)的时间复杂度,O(1)的空间复杂度对一个链表排序
- 分析
- 因为是O(nlgn)的时间复杂度,所以我们第一感觉就是快排是否可以?
- 因为对于每一段数组来说,快排即需要从前向后也需要从后向前。所以对于我们的单向链表来说有点不适合。
- 那么另一个排序方法:归并排序呢?
- 归并排序常规的做法是up-to-bottom,即先分只一个元素,然后再合并。
- 但是这对于链表来说我们需要计算mid,而我们不能在O(1)的时间复杂度得到mid,而是需要遍历才能得到第mid个元素,所以也不行。
- 所以我们能否可以考虑buttom-to-up呢?
- 对于一个链表,我们首先对长度为1的元素进行排序,然后两两合并。然后再对长度为2的子集进行切分(因为前面长度为1的时候我们进行了俩俩合并操作,所以此时这个子集内的元素是有序的),然后再进行合并。一直到我们对长度为整个list长进行切分。合并。这样我们就完成了对list 的排序。
- 对于边界情况
- 因为每次我们会得到两个长度为X的子集。如果第一个子集的起始点是H。假设说链表的长度已经不足X,则第二个子集的起始点是NULL。如果说第一个子集的起始点就是NULL,则第二个子集的起始点必定也是NULL。
- 我们得到了两个划分好的子集后,我们要对他们进行合并。如何存储合并后的结果呢?我们必须新建一个节点,他是第一个子集的起始元素的前一个A。那这样两个子集中的最小元素就是A的下一个元素。
- 代码
ListNode* sortList(ListNode* head) {
if(head == NULL || head->next == NULL)
return head;
int len = 0;
ListNode* cur = head;
while (cur){
cur = cur->next;
len += 1;
}
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* prior;
for(int step=1;step<len;step <<= 1){
ListNode* cur = dummy->next;
prior = dummy;
while(cur){
ListNode* first = cur;
ListNode* second = split(first, step);
cur = split(second, step);
prior = merge(first, second, prior);
}
}
return dummy->next;
}
ListNode* split(ListNode* head, int n){
for(int i=0;i<(n-1) && head;i++)
head = head->next;
if(head == NULL)
return NULL;
ListNode* second = head->next;
head->next = NULL;
return second;
}
ListNode* merge(ListNode* l1, ListNode* l2, ListNode* prior){
while(l1 && l2){
if(l1->val < l2->val){
prior->next = l1;
l1 = l1->next;
prior = prior->next;
}else{
prior->next = l2;
l2 = l2->next;
prior = prior->next;
}
}
if(l1)
prior->next = l1;
if(l2)
prior->next = l2;
while (prior->next){
prior = prior->next;
}
return prior;
}