题目如下:
合并 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
今天的签到题是 378. 有序矩阵中第K小的元素,依旧是暴力解题后看图解,在第二种解法使用归并,提示参考 23. 合并K个排序链表,随先解此题。
本体题解给出了 3 种解法
- 顺序合并
- 分治合并
- 优先队列合并
顺序合并较为简单,不作赘述,会在分治合并中提及,由于 C# 没有优先队列,故我也还没有理解优先队列合并。
以下列出自己解题时使用的方法和理解分治合并后的方法。
解法一
一开始使用的解法比较暴力,遍历 k 个链表头节点,每次对比出最小的头节点 head,记录下这个节点 head,接入上一次记录的头节点的 next,同时把所在链表的头节点设为 head.next。遍历以上过程,直至 k 个链表中的所有节点都被对比过。
此解法是自己写的,提交延迟达734ms,不宜使用,只作记录反思。
解法时间复杂度为总节点数 * 链表数 O(n * k),空间复杂度为创建了一个新数组存储新的头节点 O(k)。
以下为代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode MergeKLists(ListNode[] lists) {
ListNode[] nodeArr = new ListNode[lists.Length];
for (int i = 0;i< lists.Length;i++)
{
if (lists[i] != null)
{
nodeArr[i] = lists[i];
}
}
ListNode head = null;
ListNode _head = null;
bool isEmpty = false;
while (!isEmpty)
{
isEmpty = true;
int k = int.MaxValue;
int index = 0;
for (int i = 0;i < nodeArr.Length;i++)
{
if (nodeArr[i] != null)
{
isEmpty = false;
if(nodeArr[i].val < k)
{
index = i;
k = nodeArr[i].val;
}
}
}
if (!isEmpty)
{
if (head == null)
{
_head = nodeArr[index];
head = nodeArr[index];
nodeArr[index] = nodeArr[index].next;
}
else
{
head.next = nodeArr[index];
head = head.next;
nodeArr[index] = nodeArr[index].next;
}
}
}
return _head;
}
}
解法二、分治合并
相较于自己的解法一,顺序合并对合并过程做了一次规划,每次只用一条结果链表去合并新的链表。解法时间复杂度为总节点数 O(n),空间复杂度为 O(1)。
分支合并则是将 k 条链表分成 k/2 组,进行两两合并,反复该过程,第二次分为 k/4 组,第三次 k/8 组,以此类推,直至只剩一条链表即结果。
复杂度分析较为复杂,直接引用力扣解释,同时引用力扣图解:
自己编写的代码中有一处已知问题:合并结果直接赋值到传入的链表数组中,改变了数组原本的内容。另外,检查之后并未发现有开辟新的内存空间,但是提交后内存消耗只击败了50%的用户,不知道是不是语言优势,如有大佬赐教不胜感激
以下为代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode MergeKLists(ListNode[] lists) {
if (lists.Length == 0)
{
return null;
}
int k = 1;
int nodeNum = 0;
while (nodeNum != 1)
{
nodeNum = 0;
for (int i = 0;i < lists.Length;i += k * 2)
{
if (i+k >= lists.Length)
{
lists[i] = MergeTwoLists(lists[i],null);
}
else
{
lists[i] = MergeTwoLists(lists[i],lists[i + k]);
}
nodeNum ++;
}
k = k * 2;
}
return lists[0];
}
private ListNode MergeTwoLists(ListNode nodeA,ListNode nodeB)
{
if (nodeA == null || nodeB == null)
{
return nodeA == null? nodeB:nodeA;
}
ListNode head;
if (nodeA.val <= nodeB.val)
{
head = nodeA;
nodeA = nodeA.next;
}
else
{
head = nodeB;
nodeB = nodeB.next;
}
ListNode tail = head;
while (nodeA != null && nodeB != null)
{
if (nodeA.val<= nodeB.val)
{
tail.next = nodeA;
nodeA = nodeA.next;
}
else
{
tail.next = nodeB;
nodeB = nodeB.next;
}
tail = tail.next;
}
tail.next = nodeA != null? nodeA:nodeB;
return head;
}
}