题目
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入: [ 1->4->5, 1->3->4, 2->6 ] 输出: 1->1->2->3->4->4->5->6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
肯定是不能所有的链表都合并到一条链上的,可以预见,如果这样的话会导致有一条特别长的链表。
这里能想到两种链条合并的方式。
- 一是两两合并(类似淘汰赛一样),也可以理解成是分治的思想,比如8条链,合并变成4条、2条、1条,这样子操作的一个优点是能大概保证每一条链合并次数都大致相同,所以不容易出现那种特别长的链条,而且只需要写一个合并两条链的函数就可以了,实际实现也比较方便,下面代码中采用的就是这个方法,效率还行。时间复杂度也是O(kn*lgk)
- 二是采用官方题解中的优先队列。我们需要维护当前每个链表没有被合并的元素的最前面一个,k个链表就最多有 k 个满足这样条件的元素,每次在这些元素里面选取 val 属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。这样需要维护一个队列,每次挑选出k个数中的最小值,这个挑选的方式也可以采用“分治”思想解决,不过肯定是要进行(k-1)次比较。时间复杂度是O(nk*lgk)
代码:
// merge2Lists,顾名思义,把两个链表合并到一起,需要考虑到各种可能的异常
struct ListNode* merge2Lists(struct ListNode* head1, struct ListNode* head2)
{
struct ListNode * head = NULL;
struct ListNode * tail = NULL;
if (head1 == NULL) {
return head2;
}
if (head2 == NULL) {
return head1;
}
if (head1->val <= head2->val) {
head = tail = head1;
head1 = head1->next;
} else {
head = tail = head2;
head2 = head2->next;
}
while (1) {
if ((head1 == NULL) && (head2 != NULL)) {
tail->next = head2;
head2 = NULL;
break;
}
if ((head2 == NULL) && (head1 != NULL)) {
tail->next = head1;
head1 = NULL;
break;
}
if (head1->val <= head2->val) {
tail->next = head1;
tail = head1;
head1 = head1->next;
} else {
tail->next = head2;
tail = head2;
head2 = head2->next;
}
}
return head;
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize){
int left = 0;
int right = 0;
int count = 0;
if (listsSize <= 0) {
return NULL;
}
do {
count = 0;
left = 0;
right = listsSize-1;
while (left < right) {
lists[count++] = merge2Lists(lists[left++], lists[right--]);
}
listsSize = (left == right)? left+1:left;
} while (listsSize != 1);
return lists[0];
}