问题:
Sort a linked list in O(n log n) time using constant space complexity.
思路:
按时间复杂度,和空间复杂度,可以确定基本为快排和归并排序。关于算法的空间复杂度和时间复杂度有一个超赞的博客--常用的排序算法的时间复杂度和空间复杂度。
具体实现:
-------------------快排法------------------
void swap(int &a, int &b) //交换函数
{
int c = a + b;
a = c - a;
b = c - a;
}
ListNode* sort(ListNode * left, ListNode * right)
{
if(left == right ) return left;
ListNode *p1 = left,*p2 = p1->next,*p3 = NULL;
if(p2 == NULL) return left;
if(p2->val < p1->val) swap(p2->val, p1->val);
if(p2 == right) return left;
p3 = p2->next;
if(p3 == NULL) return left;
if(p3->val < p2->val) swap(p3->val, p2->val);
if(p2->val < p1->val) swap(p2->val, p1->val);
if(p3 == right) return left;
//上面的代码主要为了完成在前三个值中选取中间值作为后面的比较
ListNode * mid = p2;
for(ListNode *p = p3; p != right; )
{
ListNode *np = p->next;
if(np != NULL && np->val < mid->val) //插入到mid前
{
p->next = np->next;
np->next = left;
left = np;
}
else
{
p = p->next;
}
}
ListNode * temp = sort(mid->next,right); //递归
mid->next = temp;
ListNode * head = sort(left, mid);//递归
return head;
}
分析:快排的平均时间复杂度是符合要求的,但是最差情况时间复杂度是O(n²)。所以为了初步解决,采用对前三个数据选取中间值作为比较数据。也可以使用随机位置作为比较数据(但是链表中不合适,数组较为合适)。RumTime
1576ms,勉强过了。
鉴于不心甘啊,所以我又去写了归并排序,中间折腾了蛮久的,印象深刻啊!也进一步思考了,蛮好的。
--------------归并排序--------------
void swap(int &a, int &b)
{
a = a ^ b;
b = b ^ a;
a = a ^ b;
}
ListNode* Merger(ListNode * head1, ListNode * head2)
{
ListNode tm(0);
ListNode *head = &tm;
ListNode *p1 = head1, *p2 = head2, *ptemp = head;
while( p1 != NULL && p2 != NULL)
{
if(p1->val < p2->val)
{
ptemp->next = p1;
p1 = p1->next;
}
else
{
ptemp->next = p2;
p2 = p2->next;
}
ptemp = ptemp->next;
}
if(p1 != NULL) ptemp->next = p1;
if(p2 != NULL) ptemp->next = p2;
return head->next;
}
ListNode* MergerSort(ListNode* start)
{
if(start == NULL || start->next == NULL) return start;
ListNode *mid = start, *p= start;
for(ListNode *p = start; p!= NULL && p->next != NULL && p->next->next != NULL; p = p->next->next)
mid = mid->next; //找中间位置
ListNode *midnext = mid->next;
mid->next = NULL;
ListNode *p1 = MergerSort(start);//递归
ListNode *p2 = MergerSort(midnext);
return Merger(p1, p2); //合并
}
解析:1. 交换使用了位运算的异或,主要原理是 a = a^b^b. b^b=00...0
2. 归并算法是一种分治算法思想,对list其不断找中间值的方案是指针p1每次移2位,p2每次移1位,则p1到list尾时,p2为mid。
3. 每次递归时列表尾为NULL,方便判读,所以先保存mid->next。之后将mid->next = NULL.
4. 对于Merge两个list时,使用p->next连接。所以对于head需要特殊处理,采用先添加一个临时的无用节点,最后返回head->next.
结果:归并排序RunTime 224ms。过了
------尾声----
耗时了很久来写这个程序,很明显感受到自己的基础还不够扎实。有很多需要注意和学习的!加油~