题目:
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] 按 升序 排列
lists[i].length 的总和不超过 10^4
解答:
解答一:在一个循环中,依次将两个链表合并,合并后的结果放到其中一个链表,另一个链表设置为nullptr,代码如下,
ListNode* mergeTwoList(ListNode* l1, ListNode* l2) { if (l1 == nullptr&&l2 == nullptr) return nullptr; if (l1 == nullptr) return l2; if (l2 == nullptr) return l1; ListNode* result = new ListNode(); ListNode* tmp = result; while (l1 != nullptr || l2 != nullptr) { if (l1 == nullptr) { tmp->next = l2; l2 = l2->next; tmp = tmp->next; continue; } if (l2 == nullptr) { tmp->next = l1; l1 = l1->next; tmp = tmp->next; continue; } if (l1->val < l2->val) { tmp->next = l1; l1 = l1->next; tmp = tmp->next; continue; } else { tmp->next = l2; l2 = l2->next; tmp = tmp->next; continue; } } return result->next;; } ListNode* mergeKLists(vector<ListNode*>& lists) { if (lists.size() == 0) return nullptr; else if (lists.size() == 1) return lists.at(0); //从两侧向中间移动,两两合并 int idx1 = 0; int idx2 = lists.size()-1; ListNode* result = nullptr; while (idx1 < idx2) { while (lists.at(idx1) == nullptr && idx1 < idx2)//list中可能有空链表,删除里面的空链表 { idx1++; } while (lists.at(idx2) == nullptr && idx1 < idx2) { idx2--; continue; } if (idx1 == idx2) { if (result == nullptr && lists.at(idx1) != nullptr)//去重后,两个idx可能重复,但是重复的可能是唯一一个不为nullptr的list return lists.at(idx1); break; } //合并两个链表 result = mergeTwoList(lists.at(idx1), lists.at(idx2)); lists.at(idx1) = result; idx2--; } return result; }
提交后通过,但是合并次数为N-1,修改如下
解答二:让vector中的元素两两合并,合并后再递归调用,减少合并次数,分治法
vector<ListNode*> mergeSubStep(vector<ListNode*> lists) { vector<ListNode*> vec; for (auto val : lists) { if (val == nullptr) continue; vec.push_back(val); } if (vec.size() < 2) return vec; vector<ListNode*> tmpVec; int idx1 = 0, idx2 = vec.size() - 1; while (idx1 < idx2) { ListNode* result = mergeTwoList(vec.at(idx1), vec.at(idx2)); tmpVec.push_back(result); idx1++; idx2--; } if (vec.size() % 2 == 1)//奇数个,把中间的添加到vec { tmpVec.push_back(vec.at(idx1)); } return tmpVec; } ListNode* mergeKLists2(vector<ListNode*>& lists) { vector<ListNode*> result = mergeSubStep(lists); if (result.size() == 0) return nullptr; else if (result.size() == 1) return result.at(0); while (true) { result = mergeSubStep(result); if (result.size() < 2) break; } return result.at(0); }
其中,两个链表合并的代码跟解答一中的一样,省略。提交后,相对于解答一,速度提高了很多。
看解答。
一:合并两个链表,解答中的效率较高,代码如下:
ListNode* mergeTwoLists(ListNode *a, ListNode *b) { if ((!a) || (!b)) return a ? a : b; ListNode head, *tail = &head, *aPtr = a, *bPtr = b; while (aPtr && bPtr) { if (aPtr->val < bPtr->val) { tail->next = aPtr; aPtr = aPtr->next; } else { tail->next = bPtr; bPtr = bPtr->next; } tail = tail->next; } tail->next = (aPtr ? aPtr : bPtr); return head.next; }
该函数中,判断是否为nullptr以及比较很少,在两个链表都不为空时取较小值,当其中一个为nullptr后,直接将next指向另一个链表即可。而自己写的合并在一个while中多次判断链表是否为nullptr,效率较低。
二:解答中的分治使用两个index,这样在递归调用时不会产生中间的vector,以节约内存:而且求L+R的平均值时,使用的是右移操作,比除2效率高。
ListNode* merge(vector <ListNode*> &lists, int l, int r) { if (l == r) return lists[l]; if (l > r) return nullptr; int mid = (l + r) >> 1; return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r)); } ListNode* mergeKLists(vector<ListNode*>& lists) { return merge(lists, 0, lists.size() - 1); }