题目来源:https://leetcode.com/problems/merge-k-sorted-lists/
问题描述
23. Merge k Sorted Lists
Hard
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Example:
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6
------------------------------------------------------------
题意
合并k个有序单链表为一个有序单链表。
------------------------------------------------------------
思路
时间复杂度最低的方法是开一个新内存放合并表,从每个单链表的头指针开始一个一个选取最小的值放入合并表。如此一来,问题化归为重复N次(不妨记k个单链表总的元素个数为N)找k个指针指向的值的集合中的最小值。用最小堆优化找最小值的过程,可以将这个过程的复杂度从O(Nk)降到O(Nlogk).
具体来说,维护k个指针,分别指向k个有序单链表中未放入合并表的最前面的元素,用一个大小为k的最小堆存储k个指针指向的值。建堆的复杂度为O(k),建堆过程只执行一次. 每次弹出堆顶的元素放入合并表(不妨设堆顶的元素来自第i个单链表),并后移第i个单链表的指针,将新的元素入堆,维护最小堆。维护最小堆的复杂度为O(logk),共执行N次。所以总的复杂度为O(k + Nlogk) = O(Nlogk).
在最小堆的实现上,笔者写了两版代码,一版是自己实现的最小堆类,另一版是用java.util.PriorityQueue类(优先队列),使用PriorityQueue<T>也有两种写法,一种是用T类implements Comparable<T>接口【未注释的】,另一种是实例化PriorityQueue<T>的时候用匿名内部类implements Comparator<T>接口作为构造函数的参数【注释掉的】。详见代码。
还有就是要当心”[]”和”[[]]”这两个测试用例。
------------------------------------------------------------
代码
src1: 自己实现最小堆类的实现版本
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public final int INF = (1<<30);
class Node {
public int val;
public int index;
public Node(int val, int index)
{
this.val = val;
this.index = index;
}
public Node(Node node)
{
this.val = node.val;
this.index = node.index;
}
}
class MinHeap {
private Node[] nodeList;
private int maxSize;
private int currentSize;
public MinHeap(int maxSize, Node[] vals)
{
setMaxSize(maxSize);
makeHeap(vals);
}
public void setMaxSize(int maxSize)
{
currentSize = 0;
this.maxSize = maxSize;
}
public Node getMin()
{
return nodeList[0];
}
public void insert(Node element)
{
nodeList[0] = element;
sift(0);
}
private void sift(int loc)
{
Node mid = nodeList[loc], left = new Node(INF, -1), right = new Node(INF, -1);
if (2 * loc + 1 < currentSize)
{
left = nodeList[2 * loc + 1];
}
if (2 * loc + 2 < currentSize)
{
right = nodeList[2 * loc + 2];
}
if (mid.val <= left.val && mid.val > right.val)
{
nodeList[2 * loc + 2] = mid;
nodeList[loc] = right;
sift(2 * loc + 2);
}
else if (mid.val > left.val && mid.val <= right.val)
{
nodeList[2 * loc + 1] = mid;
nodeList[loc] = left;
sift(2 * loc + 1);
}
else if(mid.val > left.val && mid.val > right.val)
{
if (left.val <= right.val)
{
nodeList[2 * loc + 1] = mid;
nodeList[loc] = left;
sift(2 * loc + 1);
}
else
{
nodeList[2 * loc + 2] = mid;
nodeList[loc] = right;
sift(2 * loc + 2);
}
}
}
public void makeHeap(Node[] vals)
{
currentSize = vals.length;
nodeList = vals;
for (int i = currentSize/2; i >= 0; i--)
{
sift(i);
}
}
}
public ListNode mergeKLists(ListNode[] lists) {
int k = lists.length, i = 0, value = 0;
if (k == 0)
{
return null;
}
LinkedList<Node> init = new LinkedList<Node>();
ListNode head = null, ret = null;
for (i=0; i<k; i++)
{
if (lists[i] != null)
{
init.add(new Node(lists[i].val, i));
lists[i] = lists[i].next;
}
}
int heap_size = init.size();
if (heap_size == 0)
{
return null;
}
MinHeap heap = new MinHeap(heap_size, init.toArray(new Node[0]));
while (true)
{
Node cur = heap.getMin();
if (cur.val == INF)
{
break;
}
if (head == null)
{
head = new ListNode(cur.val);
ret = head;
}
else
{
head.next = new ListNode(cur.val);
head = head.next;
}
if (lists[cur.index] != null)
{
heap.insert(new Node(lists[cur.index].val, cur.index));
lists[cur.index] = lists[cur.index].next;
}
else
{
heap.insert(new Node(INF, -1));
}
}
return ret;
}
}
src2: 使用java.util.PriorityQueue类的实现版本
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
class Node {
public int val;
public int index;
public Node(int val, int index)
{
this.val = val;
this.index = index;
}
public Node(Node node)
{
this.val = node.val;
this.index = node.index;
}
}
public ListNode mergeKLists(ListNode[] lists) {
int k = lists.length, i = 0, value = 0;
if (k == 0)
{
return null;
}
PriorityQueue<Node> queue = new PriorityQueue<Node>(1, new NodeComparator implements Comparator<Node>() {
@Override
public int compare(Node n1, Node n2)
{
return n1.val - n2.val;
}
} );
ListNode head = null, ret = null;
for (i=0; i<k; i++)
{
if (lists[i] != null)
{
queue.add(new Node(lists[i].val, i));
lists[i] = lists[i].next;
}
}
if (queue.size() == 0)
{
return null;
}
while (!queue.isEmpty())
{
Node cur = queue.poll();
if (head == null)
{
head = new ListNode(cur.val);
ret = head;
}
else
{
head.next = new ListNode(cur.val);
head = head.next;
}
if (lists[cur.index] != null)
{
queue.add(new Node(lists[cur.index].val, cur.index));
lists[cur.index] = lists[cur.index].next;
}
}
return ret;
}
}