剑指 Offer II 077. 链表排序
题目描述
思路:
题目进阶要求 O ( n l o g n ) O(nlogn) O(nlogn) 的时间复杂度,那么这里正好复习一下排序算法的时间复杂度:
- O ( n 2 ) O(n^2) O(n2) 级别:选择排序、插入排序、冒泡排序;
- O ( n l o g n ) O(nlogn) O(nlogn) 级别:堆排序、快速排序、归并排序;
因此考虑堆排序、快速排序、归并排序中的一个。其中堆排序和快速排序都需要元素的随机访问,对于链表来说不合适。只能考虑归并排序。
方法一:
最简单想到的归并排序就是自顶向下归并排序。我们将链表从中间拆分成两段,然后将两段链表分别排序后再合并在一起。整个过程递归地进行,递归边界是当前链表为空或者只有一个节点,此时的链表自然是有序的。
拆分链表使用快慢指针方法,合并两个有序链表使用双指针方法。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn) ,由于使用了递归,空间复杂度 O ( l o g n ) O(logn) O(logn) 。
class Solution {
// 快慢指针法寻找链表中点,并从中点处断开,返回后半段链表的头节点
ListNode* split(ListNode* head) {
ListNode* slow = head, * fast = head;
while (fast->next != nullptr && fast->next->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
ListNode* second = slow->next;
slow->next = nullptr;
return second;
}
// 双指针法合并两条链表,使用虚拟头节点
ListNode* merge(ListNode* head1, ListNode* head2) {
ListNode* res = new ListNode();
ListNode* cur = res;
while (head1 != nullptr && head2 != nullptr) {
if (head1->val <= head2->val) {
cur->next = head1;
head1 = head1->next;
} else {
cur->next = head2;
head2 = head2->next;
}
cur = cur->next;
}
cur->next = head1 != nullptr ? head1 : head2;
return res->next;
}
public:
ListNode* sortList(ListNode* head) {
//自顶向下归并排序
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode* first = head;
ListNode* second = split(head);
first = sortList(first);
second = sortList(second);
return merge(first, second);
}
};
方法二
题目进阶还要求在常数空间内完成排序操作,如何对方法一进行空间优化呢?方法一空间消耗在递归上,那就不用递归而改用迭代方法,这就是自底向上的归并。
看了半天题解,也不会自己写,算了,等后面再研究吧。
剑指 Offer II 078. 合并排序链表数组
题目描述
思路
可以有以下几种思路:
挨个合并
先拿lists[0]
和lists[1]
合并,合并后的头节点放在lists[0]
里,然后再拿lists[0]
和lists[2]
合并,合并结果还是放在lists[0]
里,以此类推。时间复杂度
O
(
n
2
)
O(n^2)
O(n2) ;
二分法(归并排序):
首先两两捉对合并链表,这样一轮合并完成后,就变成了
n
2
\frac{n}{2}
2n 个有序链表,并且它们的头节点分别存放在下标为0, 2, 4,...
中;
再两两捉对合并链表,这样一轮合并完成后,就变成了
n
4
\frac{n}{4}
4n 个有序链表,并且它们的头节点分别存放在下标为0, 4,...
中;
最终合并完成的链表头节点存放在 lists[0]
中。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn) 。
class Solution {
ListNode* merge(ListNode* head1, ListNode* head2) {
ListNode* res = new ListNode();
ListNode* cur = res;
while (head1 != nullptr && head2 != nullptr) {
if (head1->val <= head2->val) {
cur->next = head1;
head1 = head1->next;
} else {
cur->next = head2;
head2 = head2->next;
}
cur = cur->next;
}
cur->next = head1 != nullptr ? head1 : head2;
return res->next;
}
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n = lists.size(); //共有n个有序链表
if (n == 0) {
return nullptr;
}
for (int step = 1; step < n; step *= 2) {
for (int i = 0; i + step < n; i += step * 2) {
ListNode* first = lists[i], * second = lists[i + step];
lists[i] = merge(first, second);
}
}
return lists[0];
}
};