一、暴力法
暴力法不用做太多解释,代码也不写了,主要是两个暴力法:
- 每次都合并两个链表到一个上面,再用这个新链表继续和另一个链表进行合并。这样需要合并n-1次。
- 逐一比较的方法,就是每次从这K个链表的头结点中选一个最小的,然后加到新建的链表中,重复下去。
二、优先级队列(小顶堆)
其实就是上面暴力法2的一个变种,我们如何从K个链表的头结点中选一个最小的。方法就是建立一个小顶堆,然后每次从堆顶就可以取一个最小值。删掉堆顶元素后,查看删除的元素是否还有后继结点,有则加入到小顶堆中。代码如下:
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0)
return null;
if (lists.length == 1)
return lists[0];
//一定要注意新建优先级队列时候,初始化要用的参数
//一个是堆的大小,一个是自定义的比较器
PriorityQueue<ListNode> queue = new PriorityQueue<>(lists.length, new Comparator<ListNode>() {
@Override
public int compare(ListNode o1, ListNode o2) {
if (o1.val < o2.val)
return -1;
else if (o1.val > o2.val)
return 1;
else
return 0;
}
});
//将所有不为null的队列头放到堆中
for (ListNode node : lists) {
if (node != null)
queue.offer(node);
}
ListNode result = new ListNode(-1), p = result;
while (!queue.isEmpty()) { //堆不为空时,不断地取堆顶
ListNode popNode = queue.poll();
p.next = popNode; //将堆顶数据(当前最小的)放到结果后面
p = p.next;
if (popNode.next != null) { //堆顶还有后继,则把后继加入到堆中
queue.offer(popNode.next);
}
}
return result.next;
三、归并合并
就是每次都合并两个链表,两两合并。
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0)
return null;
if (lists.length == 1)
return lists[0];
int step = 1, index = 0;
//很重要的一步,就是要知道index和step的条件
while (index + step < lists.length) {
for (; index+step < lists.length; index += 2*step) {
//将index和index+step位置的两个链表合并
lists[index] = merge2Lists(lists[index], lists[index+step]);
}
index = 0; //合并一次后,下标从0开始,step乘2
step *= 2;
}
return lists[0];
}
//合并两个有序列表的过程
public ListNode merge2Lists(ListNode list1, ListNode list2) {
ListNode NewHead = new ListNode(0), head = NewHead;
ListNode p1 = list1, p2 = list2;
while (p1 != null && p2 != null) {
if (p1.val <= p2.val) {
head.next = p1;
head = head.next;
p1 = p1.next;
} else {
head.next = p2;
head = head.next;
p2 = p2.next;
}
}
if (p1 != null) {
head.next = p1;
}
if (p2 != null) {
head.next = p2;
}
return NewHead.next;
}