在 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(nlogn)O(nlogn)和O(1)O(1),根据时间复杂度我们自然想到二分法,从而联想到归并排序;
对数组做归并排序的空间复杂度为 O(n),分别由新开辟数组O(n)和递归函数调用O(logn)组成,而根据链表特性:
数组额外空间:链表可以通过修改引用来更改节点顺序,无需像数组一样开辟额外空间;
递归额外空间:递归调用函数将带来O(logn)的空间复杂度,因此若希望达到O(1)空间复杂度,则不能使用递归。
通过递归实现链表归并排序,有以下两个环节:
分割 cut 环节: 找到当前链表中点,并从中点将链表断开(以便在下次递归 cut 时,链表片段拥有正确边界);
我们使用 fast,slow 快慢双指针法,奇数个节点找到中点,偶数个节点找到中心左边的节点。
找到中点 slow 后,执行 slow.next = None 将链表切断。
递归分割时,输入当前链表左端点 head 和中心节点 slow 的下一个节点 tmp(因为链表是从 slow 切断的)。
cut 递归终止条件: 当head.next == None时,说明只有一个节点了,直接返回此节点。
合并 merge 环节: 将两个排序链表合并,转化为一个排序链表。
双指针法合并,建立辅助ListNode h 作为头部。
设置两指针 left, right 分别指向两链表头部,比较两指针处节点值大小,由小到大加入合并链表头部,指针交替前进,直至添加完两个链表。
返回辅助ListNode h 作为头部的下个节点 h.next。
时间复杂度 O(l + r),l, r 分别代表两个链表长度。
当题目输入的 head == None 时,直接返回None。
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 public: 11 ListNode* sortList(ListNode* head) { 12 if (head == nullptr || head->next == nullptr) 13 return head; 14 ListNode *fast = head->next, *slow = head; 15 //快慢指针找到链表中点 16 while (fast!=nullptr&&fast->next!=nullptr) 17 { 18 slow = slow->next; 19 fast = fast->next->next; 20 } 21 //cut链表 22 ListNode *tmp = slow->next; 23 slow->next = nullptr; 24 25 ListNode *left = sortList(head); 26 ListNode *right = sortList(tmp); 27 28 ListNode *h = new ListNode(0); 29 ListNode *res = h; 30 while (left!=nullptr&&right!=nullptr) 31 { 32 if (left->val < right->val) 33 { 34 h->next = left; 35 left = left->next; 36 } 37 else 38 { 39 h->next = right; 40 right = right->next; 41 } 42 h = h->next; 43 } 44 h->next = left != nullptr ? left : right; 45 return res->next; 46 } 47 };
非递归版:(由下往上处理)
1 class Solution { 2 public: 3 ListNode* sortList(ListNode* head) { 4 ListNode dummyHead(0); 5 dummyHead.next = head; 6 auto p = head; 7 int length = 0; 8 while (p) { 9 ++length; 10 p = p->next; 11 } 12 13 for (int size = 1; size < length; size <<= 1) { 14 auto cur = dummyHead.next; 15 auto tail = &dummyHead; 16 17 while (cur) { 18 auto left = cur; 19 auto right = cut(left, size); // left->@->@ right->@->@->@... 20 cur = cut(right, size); // left->@->@ right->@->@ cur->@->... 21 22 tail->next = merge(left, right); 23 while (tail->next) { 24 tail = tail->next; 25 } 26 } 27 } 28 return dummyHead.next; 29 } 30 31 ListNode* cut(ListNode* head, int n) { 32 auto p = head; 33 while (--n && p) { 34 p = p->next; 35 } 36 37 if (!p) return nullptr; 38 39 auto next = p->next; 40 p->next = nullptr; 41 return next; 42 } 43 44 ListNode* merge(ListNode* l1, ListNode* l2) { 45 ListNode dummyHead(0); 46 auto p = &dummyHead; 47 while (l1 && l2) { 48 if (l1->val < l2->val) { 49 p->next = l1; 50 p = l1; 51 l1 = l1->next; 52 } else { 53 p->next = l2; 54 p = l2; 55 l2 = l2->next; 56 } 57 } 58 p->next = l1 ? l1 : l2; 59 return dummyHead.next; 60 } 61 };