难度:中等
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3 输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0 输出: -1->0->3->4->5
题目分析:
题目的要求和苛刻,时间复杂度为O(n log n),那么在我们熟悉的排序中只有快排和归并排序,再者要求空间复杂度为常数空间复杂度。那么我们知道在排序数组的时候,快排的空间复杂度为O(longn),归并排序的时间复杂度为O(n)。在这里我们显然考虑使用归并排序,因为一个是快排的话需要前后指针,那么必然需要O(n)的空间,放在vector中进行排序,那不符合要求;二个的话,在不考虑辅助空间的情况下,归并排序的空间复杂度可以为O(1)。具体实现看代码
参考代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
//这里仿写数组的归并排序
class Solution {
public:
ListNode* sortList(ListNode* head) {
//当只有一个节点或者空节点的话,就没必要也不能再一分为二了
if (head == NULL || head->next == NULL)
return head;
//因为不能直接访问中间节点,所以考虑快慢指针,注意这里快指针从第二个节点开始移动
//为什么要这么移动,画个图,这样慢指针才能指向最中间的节点(总数为单数时),
//或者指向中间靠前的节点(总数为偶数时)
ListNode* pre = head->next;
ListNode* post = head;
while (pre && pre->next)
{
pre = pre->next->next;
post = post->next;
}
//记录中间节点,和下一个节点
ListNode* mid = post;
mid = mid->next;
post->next = NULL;
//这里把sortList函数直接传入node_merge函数,这样才不会丢失节点,
//否则排序的过程中会丢失节点。因为当递归返回到上一层是,比如是3,4,mid指向的是4,
//那么返回后还是4,这样就会丢失节点,应该指向3节点
return node_merge(sortList(head), sortList(mid));
}
ListNode* node_merge(ListNode* head, ListNode* mid)
{
//如果两个节点相等时,说明是一个节点,不需要排序
if (head == mid)
return head;
//建立两个指针,分别从两个链表的头结点开始移动
ListNode* left = head;
ListNode* right = mid;
//这里需要建立一个临时头结点来进行连接,因为你不知道上面两个链表哪个是头结点,
//所以建立一个临时头结点,后面两个链表再比大小,将小的先连接上去,以此类推
ListNode* temp_head = new ListNode(INT_MIN);
ListNode* res = temp_head;
while (left && right)
{
if (left->val < right->val)
{
temp_head->next = left;
temp_head = temp_head->next;
left = left->next;
}
else{
temp_head->next = right;
temp_head = temp_head->next;
right = right->next;
}
}
while (left)
{
temp_head->next = left;
temp_head = temp_head->next;
left = left->next;
}
while (right)
{
temp_head->next = right;
temp_head = temp_head->next;
right = right->next;
}
return res->next;
}
};