算法LeetCode解題(C++)-19. 合併K個排序鏈表(難度:困難)

題目描述:

合併 k 個排序鏈表,返回合併後的排序鏈表。請分析和描述算法的複雜度。

示例:

輸入:
[
  1->4->5,
  1->3->4,
  2->6
]
輸出: 1->1->2->3->4->4->5->6

這道題讓我們合併k個有序鏈表,最終合併出來的結果也必須是有序的,之前做過一道 Merge Two Sorted Lists,是混合插入兩個有序鏈表。這道題增加了難度,變成合並k個有序鏈表了,但是不管合併幾個,基本還是要兩兩合併。那麼首先考慮的方法是能不能利用之前那道題的解法來解答此題。答案是肯定的,但是需要修改,怎麼修改呢,最先想到的就是兩兩合併,就是前兩個先合併,合併好了再跟第三個,然後第四個直到第k個。這樣的思路是對的,但是效率不高,沒法通過 OJ,所以只能換一種思路,這裏就需要用到分治法 Divide and Conquer Approach。簡單來說就是不停的對半劃分,比如k個鏈表先劃分爲合併兩個 k/2 個鏈表的任務,再不停的往下劃分,直到劃分成只有一個或兩個鏈表的任務,開始合併。舉個例子來說比如合併6個鏈表,那麼按照分治法,首先分別合併0和3,1和4,2和5。這樣下一次只需合併3個鏈表,再合併1和3,最後和2合併就可以了。代碼中的k是通過 (n+1)/2 計算的,這裏爲啥要加1呢,這是爲了當n爲奇數的時候,k能始終從後半段開始,比如當 n=5 時,那麼此時 k=3,則0和3合併,1和4合併,最中間的2空出來。當n是偶數的時候,加1也不會有影響,比如當 n=4 時,此時 k=2,那麼0和2合併,1和3合併,完美解決問題,參見代碼如下:

解法一:

ListNode* MergeKLists::mergeKLists(vector<ListNode*>& lists) {
	if (lists.empty()) return nullptr;

	int n = lists.size();
	while (n > 1)
	{
		int k = (n + 1) / 2;
		for (int i = 0; i < n / 2; ++i)
		{
			lists[i] = mergeTwoLists(lists[i], lists[i + k]);
		}
		n = k;
	}
	return lists[0];
}

ListNode* MergeKLists::mergeTwoLists(ListNode *l1, ListNode *l2)
{
	ListNode *ret = new ListNode(-1), *cur = ret;//cur作爲中間量,每次給ret添入一個值

	while (l1 && l2)
	{
		if (l1->val < l2->val) {
			cur->next = l1;
			l1 = l1->next;
		}
		else {
			cur->next = l2;
			l2 = l2->next;
		}
		cur = cur->next;
	}
	cur->next = l1 ? l1 : l2;
	return ret->next;

}

我們再來看另一種解法,這種解法利用了最小堆這種數據結構,首先把k個鏈表的首元素都加入最小堆中,它們會自動排好序。然後每次取出最小的那個元素加入最終結果的鏈表中,然後把取出元素的下一個元素再加入堆中,下次仍從堆中取出最小的元素做相同的操作,以此類推,直到堆中沒有元素了,此時k個鏈表也合併爲了一個鏈表,返回首節點即可,參見代碼如下:

ListNode* MergeKLists::mergeKLists(vector<ListNode*>& lists) {
	auto cmp = [](ListNode*& a, ListNode*& b) {
		return a->val > b->val;
	};
	priority_queue<ListNode*, vector<ListNode*>, decltype(cmp) > q(cmp);
	for (auto node : lists) {
		if (node) q.push(node);
	}
	ListNode *dummy = new ListNode(-1), *cur = dummy;
	while (!q.empty()) {
		auto t = q.top(); q.pop();
		cur->next = t;
		cur = cur->next;
		if (cur->next) q.push(cur->next);
	}
	return dummy->next;
}

運行數據分析:

做IT行業,不管是測試、開發、運維等等,或簡單或複雜的算法是必不可少的,也是大家面試工作中的必要環節,這個專欄開始和大家一起來研究著名的LeetCode,裏邊有上千種最常見的算法,面試工作出現機率很高,值得掌握研究,每次完成博客更新我會同步更新我的個人Github上的代碼,每個算法都可以直接運行調試以供掌握,GitHub地址:https://github.com/cuiguangwei/LeetCodeProject.git,歡迎大家一起學習討論進步!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章