这道题解法有很多,目前知道的最佳时间复杂度是N*logk, N表示所有链表节点总个数
解法一:建立一个size为k的最小堆,每次从最小堆中获取最小的元素,然后进行调整,每个元素会调整一次,最终复杂度是N*logk
解法二:将链表以2个为一组进行配对,为所有配对的两个链表进行合并,第一轮之后会得到k/2个有序链表,然后再进行第二轮第三轮,一直到链表个数合并为一个。
这种方法时间复杂度也是N*logk,因为每一轮合并时间复杂度都是N=L1+L2+......+Ln, 一共有logk轮
解法一算法步骤:
1.对k个链表进行处理,去掉空链表,得到一个有实际数据的链表,处理方法是将空链表全部移到数组尾部,用一个size变量表示实际有效大小;
然后进行特殊情况判断,即为空则返回,若只有一个链表,则返回该链表表头
2.基于上述链表的头节点建最小堆
2.1首先要熟悉堆数据结构,熟悉堆当前节点和其子节点的序号关系, 如果是从0开始表示,那么
left = (parent +1) * 2 - 1
right = left + 1
parent = int(son+1)/2) - 1 左右子树通用
建堆是从最后一个节点的母节点开始往前逐个调整
源码:
for (int i = (int)((size + 1)/2) - 1; i>=0; i--) {
adjust_small_heap(lists, i, size-1);
}
注意第二个参数是i,而不是0
2.2 堆调整函数,要点
2.2.1.一共三种情况,第一种当前节点分别有左右子树,只有左子树,没有子树
第二三种处理起来比较简单就不说了,第一种情况 需要先确认左右子树哪个数据比较小,记录下小的那个数的索引,再与当前节点比较,
这里记住是记录小的索引就行了,不要用布尔值去记录左右子树的大小,不然写起来会更复杂
3.获取堆顶元素作为新链表表头,之后进行各类条件判断处理,主要有两种情况
3.1 头节点元素后移,该节点已经是链表的最后一个节点,需要将该链表从堆中移除,然后进行堆调整,堆删除操作具体步骤是,将堆第一个和最后一个元素进行替换,然后size-1
3.1.1 进行删除操作后,堆大小(也就是有效链表数量)会变化,如果变为一,这个时候把这个链表补到新链表后面推出即可
3.1.2 如果堆大小大于1,则继续往后
3.2 该节点不是最后一个节点,则直接后移,然后进行堆调整
4.循环逐个获取下一个最小值,循环内的操作与三基本相同
解法一源码:
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode *ret = NULL;
int size = lists.size();
int start = 0;
while (size > start) {
if (lists[start] == NULL) {
swap(lists[start],lists[size - 1]);
size--;
} else {
start++;
}
}
if (size == 0) {
return NULL;
} else if (1 == size) {
return lists[0];
}
for (int i = (int)((size + 1)/2) - 1; i>=0; i--) {
adjust_small_heap(lists, i, size-1);
}
ret = lists[0];
lists[0] = lists[0]->next;
if (lists[0] == NULL) {
swap(lists[0],lists[size - 1]);
size--;
if (1 == size) {
ret->next = lists[0];
return ret;
}
}
adjust_small_heap(lists, 0, size-1);
ListNode *cur_ret = ret;
while (!lists.empty()) {
cur_ret->next = lists[0];
cur_ret = cur_ret->next;
lists[0] = lists[0]->next;
if (lists[0] == NULL) {
swap(lists[0],lists[size-1]);
size--;
if (1 == size) {
cur_ret->next = lists[0];
return ret;
}
}
adjust_small_heap(lists, 0, size-1);
}
return ret;
}
void adjust_small_heap(vector<ListNode*> &heap, int start, int end) {
int cur = start;
while (cur <= end) {
int left = (cur + 1) * 2 - 1;
int right = left + 1;
if (right <= end) {//有左右子树
int swap_index = 0;
if (heap[left]->val < heap[right]->val) {
swap_index = left;
} else {
swap_index = right;
}
if (heap[cur]->val <= heap[swap_index]->val) {
return;
} else {
swap(heap[cur],heap[swap_index]);
cur = swap_index;
}
} else if (left <= end) {//只有左子树
if (heap[cur]->val > heap[left]->val) {
swap(heap[cur], heap[left]);
}
return;
}
else {//叶子节点
return;
}
}
}
解法2源码:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int size = lists.size();
if (size == 0) {
return nullptr;
}
if (size == 1) {
return lists[0];
}
queue<ListNode*> waiting(deque<ListNode*>(lists.begin(), lists.end())); //将vector转为队列
//如果队列元素大于1,则取出两个进行合并,合并后的链表继续添加到链表尾部
while (waiting.size() > 1) {
ListNode *l1 = waiting.front();
waiting.pop();
ListNode *l2 = waiting.front();
waiting.pop();
waiting.push(merge2(l1, l2));
}
return waiting.front();
}
ListNode* merge2(ListNode *l1, ListNode *l2) {
ListNode *root = new ListNode(0), *pre = root;
while (l1 && l2) {
if (l1->val <= l2->val) {
pre->next = l1;
l1 = l1->next;
} else {
pre->next = l2;
l2 = l2->next;
}
pre = pre->next;
}
pre->next = l1 ? l1 : l2;
return root->next;
}