原题目
Sort a linked list in O(n log n) time using constant space complexity.
中文大意
对一个链表进行排序,要求算法的时间复杂度是O(n log n),空间复杂度是常数
题解
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *dummy = new ListNode(-1);
ListNode *ans = dummy;
while(l1!=NULL && l2!=NULL)
{
if(l1->val > l2->val)
{
dummy->next = l2;
l2 = l2->next;
}
else
{
dummy->next = l1;
l1 = l1->next;
}
dummy = dummy->next;
}
while(l1!=NULL)
{
dummy->next = l1;
l1 = l1->next;
dummy = dummy->next;
}
while(l2!=NULL)
{
dummy->next = l2;
l2 = l2->next;
dummy = dummy->next;
}
ListNode *temp = ans;
ans = ans->next;
delete temp;
return ans;
}
ListNode* sortList(ListNode* head)
{
if(head == NULL) return NULL;
if(head->next ==NULL) return head;
//利用快慢指针来定位链表的中点
ListNode *tail = head;
ListNode *mid = head;
while(tail->next!=NULL && tail->next->next!=NULL)
{
tail = tail->next->next;
mid = mid->next;
}
tail = mid;
mid = mid->next;
tail->next = NULL; //将链表从中间断开
//分治求解问题
ListNode* l1 = sortList(mid);
ListNode* l2 = sortList(head);
//将两个排序好的子链表合并
return mergeTwoLists(l1,l2);
}
};
思路
- 本题的要求的是在O(n log n)下解决问题,因此,在常用的排序算法(快速排序与归并排序)中, 快速排序法的最坏情况是O(N^2),不适用与本题,而且,从数据结构的本身来说,对于链表最优的排序方法基本上就是归并排序,因为不需要像数组一样,额外分配空间。自然地,用归并来实现链表排序,算法空间复杂度也是O(1)的,符合本题目要求
- 得出这样的结论以后,就可以使用常规的归并排序思路来解这道题,典型的分治法。首先通过快慢指针来找到链表的中点,然后将链表断开,一分为二,变成两个新的链表,再对子链表进行划分。最后再将两个链表用上一篇文章中提到的方法合并在一起
- 值得注意的是这里的递归边界条件:当链表中头为空或者链表中只有一个元素,就不再执行递归
心得体会
经过这道题才发现原来在Leetcode中可以直接通过算法的入口函数实现递归,比如这道题的入口是sortList,可以直接用sortList作为递归函数