題干
Merge k Sorted Lists
Difficulty: Hard
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
合并K個已經排序的數組。
解題思路:
難度感覺算不上Hard(畢竟我做不出來的都是Hard),不過為了方便調試這次用了.net來寫。
拿到題目最開始的反應是對于數組中的每一個鏈表都定義一個指針,每次遍歷所有的指針,記錄最小的值以及出現的位置,遍歷完成后將最小值插入結果鏈表中,如果遍歷后發現每一個指針都指向空節點則說明遍歷完成。
最后成品:
public ListNode MergeKLists(ListNode[] lists)
{
ListNode head = null, cursor = null;
while (true)
{
var indexs = new List<int>();
var min = int.MaxValue;
for (var i = 0; i < lists.Length; i++)
{
if (lists[i] == null)
{
continue;
}
if (lists[i].val > min)
{
continue;
}
else
{
if (lists[i].val < min)
{
indexs.Clear();
min = lists[i].val;
}
indexs.Add(i);
}
}
if (min == int.MaxValue)
{
break;
}
else
{
for (var i = 0; i < indexs.Count; i++)
{
if (head == null)
{
head = new ListNode(min);
cursor = head;
}
else
{
cursor.next = new ListNode(min);
cursor = cursor.next;
}
lists[indexs[i]] = lists[indexs[i]].next;
}
}
}
return head;
}
Runtime: 112 ms 91.21%
時間復雜度O(K*log(N))
K是鏈表數組的長度,N是節點數量。
Memory Usage: 28.8 MB 8.33%
空間復雜度O(K+N)
其中O(N)指的是最后長度為N的排序后數組。
O(K)則是我為了存儲指針位置而新建的List。
時間上我很滿意了,但是空間復雜度看上去很高,試著去減少一下。
結果鏈表的空間肯定是不能省的,那么只能省去List了,每次只push一個最小值進去,只記錄最小值以及最小值的位置
public ListNode MergeKLists(ListNode[] lists)
{
ListNode head = null, cursor = null;
while (true)
{
var index = -1;
var min = int.MaxValue;
for (var i = 0; i < lists.Length; i++)
{
if (lists[i] == null)
{
continue;
}
if (lists[i].val >= min)
{
continue;
}
else
{
min = lists[i].val;
index = i;
}
}
if (index == -1)
{
break;
}
else
{
if (head == null)
{
head = new ListNode(min);
cursor = head;
}
else
{
cursor.next = new ListNode(min);
cursor = cursor.next;
}
lists[index] = lists[index].next;
}
}
return head;
}
Runtime: 584 ms
Memory Usage: 29.3 MB
這樣做肯定增加時間的,時間復雜度應該為(O(KN)),但是空間復雜度應該能縮減為O(N)。
但是結果讓我大跌眼鏡,空間占用不減反增,這里是實在不能理解。
其余解法
回去翻看LeetCode上的solution,他列出了五種解法。
最簡單的應該就是暴力求解了,將所有的鏈表中的節點都push到一個數組中,排序后生成新的鏈表。
時間O(N*log(N)),空間O(N)。
另一個Smart Play則是將問題簡化為(K-1)次合并兩個排序數組,我個人覺得思路比我的好,很多復雜問題都能簡化為多個簡單問題的累加。時間復雜度O(KN),空間復雜度O(1)。
最后一種解法則是上一個方法的改進,每次合并floor(k/2)個數組,這樣合并次數就被減少到ceil(log2(k))次了。這樣就減少了數組中大部分鏈表的結點需要排序的次數,而后幾個鏈表的元素排序次數則會略有增加,大部分情況下優于上面的解。除非最后一個鏈表節點特別多,其余鏈表節點特別少,這樣的話上一個做法會優于這個。