题目内容
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例
示例 1:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
示例 3:
输入:head = []
输出:[]
提示
1、链表中节点的数目在范围 [0, 5 * 10^4] 内
2、-10^5 <= Node.val <= 10^5
题解
首先,题目要求使用O(n log n) 时间复杂度和O(1)的空间复杂度,因此在所有的排序算法中,只有归并排序并且按照迭代的要求进行才满足要求。
然后,思路如下:
第一次,将整个区间分成连续的若干段,每段长度是2:[a0,a1],[a2,a3],…[an−2,an−1], 然后将每一段内排好序,小数在前,大数在后;
第二次,将整个区间分成连续的若干段,每段长度是4:[a0,…,a3],[a4,…,a7],…[an−4,…,an−1],然后将每一段内排好序,这次排序可以利用之前的结果,相当于将左右两个有序的半区间合并,可以通过一次线性扫描来完成;
依此类推,直到每段小区间的长度大于等于 n 为止;
另外,当 n 不是2的整次幂时,每次迭代只有最后一个区间会比较特殊,长度会小一些,遍历到指针为空时需要提前结束。
代码
class Solution { public: ListNode* sortList(ListNode* head) { int n = 0; for(auto p = head; p ; p = p->next) n ++; auto dummy = new ListNode(-1); dummy->next = head; for(int i = 1; i < n; i *= 2 ){ auto cur = dummy; for(int j = 0; j + i < n; j += 2 * i){ auto first = cur->next, second = first; for(int k = 0; k < i; k ++) second = second->next; int f = 0, s = 0; while(f < i && s < i && first && second){ if(first->val <= second->val) cur = cur->next = first,first = first->next,f ++; else cur = cur->next = second,second = second->next,s++; } while(f < i && first) cur = cur->next = first,first = first->next,f ++; while(s < i && second) cur = cur->next = second,second = second->next,s ++; cur->next = second; } } return dummy->next; } };
|