https://oj.leetcode.com/problems/sort-list/点击打开链接
题目描述:
Sort a linked list in O(n log n) time using constant space complexity.
单链表排序要求时间复杂度O(n log n),并归排序时间复杂度满足要求,并且并归排序比较适合链表。
使用并归排序需要解决如下3个问题:
1. 问题的递归描述
2. 链表的划分
3. 两个有序链表的归并
以上三个问题都是经典的基本问题。
两个有序链表的归并 :将两个有序的单链表并归成为一个链表
ListNode * merge(a, b)
ListNode node, *t = &node;
if (!a) return b;
if (!b) return a;
while (a && b)
if (a->val < b->val) t->next = a; t = a; a = a->next
else t->next = b; t = b; b = b->next;
t->next = a ? a : b;
return node.next;
C语言代码如下
ListNode* merge(ListNode *a, ListNode *b)
{
ListNode node(0), *t = &node;
if (!a)
{
return b;
}
if (!b)
{
return a;
}
while (a && b)
{
if (a->val < b->val)
{
t->next = a; a = a->next;
t = t->next;
}
else
{
t->next = b; b = b->next;
t = t->next;
}
}
t->next = a ? a : b;
return node.next;
}
链表的划分问题: 将链表划分成大小相当的两个子链表。
基本思想: 采用slow, fast指针的方案。
每次移动的策略 slow = slow->next; fast = fast->next ? fast->next->next : NULL
当fast 移动到结尾 即 fast==NULL时, slow指向后一半链表的表头。
inital :
fast = slow = head;
prv = NULL prv指向前半段链表的表尾元素
while fast
prv = slow;
slow = slow->next; fast = fast->next ? fast->next->next : NULL
end while
if prv prv->next = NULL
C语言代码
ListNode* mid_list(ListNode *h) /* 返回前半段链表的尾元素指针 */
{
if (!h || !h->next)
{
return NULL;
}
ListNode *slow, *fast, *prv;
slow = fast = h; prv = NULL;
while (fast)
{
prv = slow;
slow = slow->next;
fast = fast->next ? fast->next->next : NULL;
}
return prv;
}
问题的递归描述 并归排序链表 链表的表头head
sort(head) 对链表进行排序,返回排序后链表的指针。
sort(head)
divide_list(head, head1, head2);
return merge(sort(head1), sort(head2))
ListNode* sortList(ListNode *head) { if (!head || !head->next) { return head; } ListNode *mid = mid_list(head); ListNode *back = mid->next; mid->next = NULL; ListNode *front = head; return merge(sortList(front), sortList(back)); }