最优解法没做出来,插入排序前面做过了。
参考做法
使用归并排序,有两种方法:一种是自顶向下,另一种是自底向上。
如果要达到常数级空间复杂度,那么就需要使用自底向上的解法。
自顶向下
算法思想
递归地从中间拆分链表,然后进行排序,再合并两个有序链表。
找链表中间节点可以使用快慢指针,快指针走两步,慢指针走一步,当快指针到达末尾,慢指针指向链表中间节点。
设中间节点为mid,记下mid的下一个节点,然后将mid->next设为null。
分别排序前后两个链表,然后进行合并。
注意:有一种特殊情况就是:当链表有两个节点时,mid最终会指向第二个结点,会发生错误,因为递归排序前后两个链表时,前面链表还是原链表,后面链表是空。那么它会一直卡在两个节点的链表这里,陷入死循环。
所以本来递归地终止条件是当传入链表为空,或只有一个节点。
为了避免上述特殊情况,再加上一个条件就是当链表只有两个节点时:比较前后两个节点的值,升序,则直接返回;否则,交换两个节点,返回头节点。
算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| class Solution { public: ListNode* sortList(ListNode* head) { return sort(head); }
ListNode * sort(ListNode * head) { if(head == nullptr || head->next == nullptr) { return head; } if(head->next->next == nullptr) { if(head->val < head->next->val) { return head; } else { ListNode * node = head->next; head->next = nullptr; node->next = head; return node; } } ListNode * slow = head; ListNode * fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; } ListNode * mid = slow; ListNode * next_mid = mid->next; mid->next = nullptr; return merge(sortList(head), sortList(next_mid));
}
ListNode * merge(ListNode * l1, ListNode * l2) { if(l1 == nullptr) return l2; if(l2 == nullptr) return l1;
ListNode * dummy = new ListNode(); ListNode * node = dummy, * node1 = l1, * node2 = l2; while (node1 != nullptr && node2 != nullptr) { if(node1->val < node2->val) { node->next = node1; node1 = node1->next; } else { node->next = node2; node2 = node2->next; } node = node->next; } if (node1 != nullptr) { node->next = node1; } if(node2 != nullptr) { node->next = node2; } return dummy->next; }
};
|
也可以在找中间节点的时候避免这种情况,就是将上述的while条件fast && fast->next
改成fast->next && fast->next->next
。然后删掉处理 链表只有两个节点的特殊情况的代码即可。
那么sort函数写成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ListNode * sort(ListNode * head) { if(head == nullptr || head->next == nullptr) { return head; } ListNode * slow = head; ListNode * fast = head; while (fast->next && fast->next->next) { slow = slow->next; fast = fast->next->next; } ListNode * mid = slow; ListNode * next_mid = mid->next; mid->next = nullptr; return merge(sortList(head), sortList(next_mid));
}
|
性能分析
时间复杂度:O(NlogN)。
空间复杂度:递归栈存了$$logN$$个节点。时间复杂度为O(logN)。
自底向上
算法思想
首先求得链表的长度length。然后将链表拆分成子链表,进行合并。
- 使用subLength代表每次需要排序的子链表的长度,初始subLength = 1
- 每次将链表拆分成若干个长度为subLength的子链表(最后一个子链表有可能小于subLength,如果除不尽)按照每两个子链表一组进行合并,合并后可以得到若干个长度为 subLength * 2 的有序子链表(最后一个子链表长度有可能小于subLength * 2)。
- 将subLength值加倍,重复第二步,直到subLength的值大于或等于length。
整个链表排序完成。
简单说就是:两个链表一组进行合并。初始每个链表长度为1,合并后是若干个长度为2的有序子链表;然后再两个一组,这时,每个子链表长度为2,合并后是若干个长度为4的有序子链表。一直合并下去,直到合并后的有序子链表长度大于等于链表总长度。
算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| class Solution { public: ListNode* sortList(ListNode* head) { if(head == nullptr || head->next == nullptr) { return head; } int length = 0; ListNode * node = head; while (node) { length++; node = node->next; } ListNode * dummy = new ListNode(0); dummy->next = head;
for(int subLength = 1; subLength < length; subLength *= 2) { ListNode * pre = dummy, * cur = dummy->next; while (cur != nullptr) { ListNode * head1 = cur; for (int i = 1; i < subLength && cur->next != nullptr; i++) { cur = cur->next; } ListNode *head2 = cur->next; cur->next = nullptr; cur = head2; for (int i = 1; i < subLength && cur != nullptr && cur->next != nullptr; i++) { cur = cur->next; } ListNode *next = nullptr; if (cur != nullptr) { next = cur->next; cur->next = nullptr; } ListNode *merged = merge(head1, head2); pre->next = merged; while (pre->next != nullptr) { pre = pre->next; } cur = next; } } return dummy->next; }
ListNode * merge(ListNode * l1, ListNode * l2) { if(l1 == nullptr) return l2; if(l2 == nullptr) return l1;
ListNode * dummy = new ListNode(); ListNode * node = dummy, * node1 = l1, * node2 = l2; while (node1 != nullptr && node2 != nullptr) { if(node1->val < node2->val) { node->next = node1; node1 = node1->next; } else { node->next = node2; node2 = node2->next; } node = node->next; } if (node1 != nullptr) { node->next = node1; } if(node2 != nullptr) { node->next = node2; } return dummy->next; }
};
|
性能分析
时间复杂度:O(NlogN)。
空间复杂度:O(1)。