LintCode第104题-合并k个排序链表
描述
合并 k
个排序链表(序列为升序序列),并且返回合并后的排序链表(序列为升序序列)。尝试分析和描述其复杂度.
样例 1:
输入:
lists = [2->4->null,null,-1->null]
输出:
-1->2->4->null
解释:
将2->4->null、null和-1->null合并成一个升序的链表。
样例 2:
输入:
lists = [2->6->null,5->null,7->null]
输出:
2->5->6->7->null
解释:
将2->6->null、5->null和7->null合并成一个升序的链表
第一种:顺序合并法
思路:从 lists
中取出第一个链表 currentListNode.
然后依次用 JoinAnotherList()方法
与其他链表合并.
JoinAnotherList()
是标准的两个有序链表合并逻辑,使用一个 tail
指针控制结果链表
第二种:最小堆法
思路:
初始化小根堆,把每条链表的 头节点 放进去。
每次从堆中 poll()
出选出当前最小的节点。
把它接到结果链表的尾部。
如果该节点还有 next
节点,也放入堆中-即把当前链表的后续节点也放进去。
重复以上过程直到堆为空。
顺序合并法代码如下:
/**
* Definition for ListNode.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int val) {
* this.val = val;
* this.next = null;
* }
* }
*/
public class Solution {
/**
* @param lists: a list of ListNode
* @return: The head of one sorted list.
*/
public ListNode mergeKLists(List<ListNode> lists) {
// write your code here
//双链表的插入 由于题目已知每个链表是升序的
//依次将每个ListNode进行插入
if(lists.size()<=0)
{
return null;
}
ListNode currentListNode=lists.get(0);
for(int i=1;i<lists.size();i++)
{
currentListNode=JoinAnotherList(currentListNode,lists.get(i));
}
return currentListNode;
}
public ListNode JoinAnotherList(ListNode initialListNode,ListNode anotherListNode)
{
// 虚拟头结点,避免空指针
if (initialListNode == null) return anotherListNode;
if (anotherListNode == null) return initialListNode;
// 初始化头指针和尾指针
ListNode returnList = null;
ListNode tail = returnList;
ListNode firstListNode = initialListNode;
ListNode secondListNode = anotherListNode;
//给 returnList 和 tail 赋初值
if (firstListNode.val <= secondListNode.val) {
returnList = tail = firstListNode;
firstListNode = firstListNode.next;
} else {
returnList = tail = secondListNode;
secondListNode = secondListNode.next;
}
while(firstListNode!=null&&secondListNode!=null)
{
if(firstListNode.val<=secondListNode.val)
{
tail.next=firstListNode;
firstListNode=firstListNode.next;
}else
{
tail.next=secondListNode;
secondListNode=secondListNode.next;
}
tail = tail.next;
}
while(firstListNode!=null)
{
tail.next=firstListNode;
firstListNode=firstListNode.next;
tail = tail.next;
}
while(secondListNode!=null)
{
tail.next=secondListNode;
secondListNode=secondListNode.next;
tail = tail.next;
}
return returnList;
}
}
堆排序法代码如下:
首先我们需要明确一点
Comparator 在处理不同的数据结构使用 Comparator 时,底层会调用不同的排序算法
使用场景 | 是否用 Comparator | 底层排序算法 |
---|
Arrays.sort(Object[], Comparator) | 是 | TimSort(归并 + 插入排序优化) |
Collections.sort(List, Comparator) | 是 | TimSort |
TreeSet , TreeMap | 是 | 红黑树(不是真正意义的排序,而是二叉搜索树插入) |
PriorityQueue | 是 | 堆排序(最小堆) |
而当前的堆排序插入删除时间复杂度是log(n) 查询是log(1)但堆排序不是稳定的排序算法这一点需要注意在不同场景的适用性
public class Solution {
private Comparator<ListNode> ListNodeComparator=new Comparator<ListNode>()
{
public int compare(ListNode currentLeftNode,ListNode currentRightNode ) //compare是固定写法 左边减去右边 意思是
如果返回 负数:currentLeftNode < currentRightNode
(左边小 → 左边优先)
如果返回 0:两者相等
如果返回 正数:currentLeftNode > currentRightNode
(右边小 → 右边优先)
{
return currentLeftNode.val-currentRightNode.val;
}
};
public ListNode mergeKLists(List<ListNode> allLists)//其中mergeKLists是平台要求必须以其为名
{
if(allLists==null || allLists.size()==0)
{
return null;
}
//首先通过比较器获取当前每个链表在堆中的节点
Queue<ListNode> heapListNodePriorityQueue=new PriorityQueue<ListNode>(allLists.size(),ListNodeComparator);
for(int i=0;i<allLists.size();i++)
{
if(allLists.get(i)!=null)
{
heapListNodePriorityQueue.add(allLists.get(i));//通过不停的比较 拿到当前所有list列表的最小值的节点
}
}
//虚节点 没有任何意思 只是为了操作方便 类似于单链表的头结点 方便数据的操作
ListNode vitrualNode=new ListNode(0);
//首先将所有链表的第一个节点拿出来组成堆
ListNode tail=vitrualNode;
while(!heapListNodePriorityQueue.isEmpty())
{
//拿出当前的最小节点
ListNode head=heapListNodePriorityQueue.poll();
tail.next=head;//类似于单链表构成下一个节点
tail=head;//再以当前节点为起点
if(head.next!=null)
{
heapListNodePriorityQueue.add(head.next);//让其heapListNodePriorityQueue又重新排序
}
}
return vitrualNode.next;
}
}