先贴上LeetCode原题链接:
链表排序
注意:原题要求是时间复杂度O(nlogn),空间复杂度O(1)
两种解法时间复杂度都是O(nlogn),递归的空间复杂度为O(n),迭代的空间复杂度为O(1)
解法一:归并排序(递归)
关于递归的通用思路:
- 先找终止条件,如该题中终止条件head == NULL || head->next ==NULL
- 思考返回值,每一级递归向上要返回什么信息
- 单步怎么写,也是最关键的步骤,在该题中也就是如何将两个排好序的链表合并。
通过递归实现链表归并排序,有以下两个环节:
-
分割cut环节:找到当前链表中点,并从中点将链表断开
- 因为链表无法使用索引来随机访问,所以我们使用
slow,fast
快慢指针法来找到中间节点,找到中点slow
后,执行slow->next = NULL
将链表切断。 - cut递归的终止条件:
head->next == NULL
,说明只有一个节点了,直接返回节点。
- 因为链表无法使用索引来随机访问,所以我们使用
-
合并merge环节:将两个排序链表合并,重要性不再强调,要能够熟练掌握。
template<typename T>
struct ListNode
{
T value;
ListNode<T>* next ;
ListNode(T val) : value(val),next(NULL){}
};
template<typename T>
ListNode<T>* listsort(ListNode<T> *head){
if(head == NULL || head->next == NULL)
return head;
ListNode<T> *slow = head;
ListNode<T> *fast = head->next;
while(fast != NULL && fast->next != NULL){
slow = slow->next;
fast = fast->next->next;
}
ListNode<T> *tmp = slow->next;
slow->next = NULL;
ListNode<T> *left = listsort(head);
ListNode<T> *right = listsort(tmp);
//合并两个有序链表
ListNode<T>* h = new ListNode<T>(-1);
ListNode<T> *cur = h;
while (left != NULL && right != NULL)
{
if(left->value < right->value)
{
cur->next = left;
left = left->next;
}
else
{
cur->next = right;
right = right->next;
}
cur = cur->next;
}
cur->next = left != NULL ? left : right;
return h->next;
}
解法二:归并排序(迭代)—自底而上
合并的过程上面已经讲过,这里的关键在于链表的断链,再挂接
- 分割cut环节:分割的size从1迭代到链表length,直接上示意图
ListNode* listsort(ListNode* head)
{
ListNode dummyHead(0);
dummyHead.next = head;
auto p = head;
int length = 0;
while(p)
{
++length;
p=p->next;
}
cout<<"length = "<<length<<endl;
for(int size = 1;size < length;size <<=1)
{
auto cur = dummyHead.next;
auto tail = &dummyHead;
while (cur)
{
//断链
auto left = cur;
auto right = cut(cur ,size);
cur = cut(right,size);//更新下一次while循环的头指针
//双路归并,输出归并后的头、尾指针
ListNode* mergedHead = NULL;
ListNode* mergedTail = NULL;
merge(left,right,mergedHead,mergedTail);
//挂接
tail->next = mergedHead;
tail = mergedTail;//更新下一次while循环的尾指针
}
}
return dummyHead.next;
}
ListNode* cut(ListNode* head, int size)
{
ListNode* cur = head;
while(--size && cur)
{
cur = cur->next;
}
if(!cur) return NULL;
auto next = cur->next;
cur->next = NULL;
return next;
}
void merge(ListNode* left, ListNode* right, ListNode*& head, ListNode*& tail)
{
ListNode dummyHead(0);
auto p = &dummyHead;
while(left&&right)
{
if(left->value < right->value)
{
p->next = left;
left = left->next;
}else
{
p->next = right;
right = right->next;
}
p = p->next;
}
p->next = left ? left : right;
while(p->next)
{
p = p->next;
}
head = dummyHead.next;
tail = p;
}