思路一:暴力法解决
1.先将数组中所有的list中的节点全部转存到ArrayList中
2.通过Collections.sort对数组中的元素进行排序
3.根据数组中元素个数,边遍历边重新生成ListNode
第一步遍历时间复杂度O(n),空间复杂度O(n)
第二步进行排序时间复杂度O(nlogn),空间复杂度取决于算法选择
第三步遍历生成ListNode时间复杂度O(n),空间复杂度O(n)
综上:时间复杂度O(nlogn),空间复杂度O(n)
//暴力破解
public ListNode mergeKLists(ListNode[] lists) {
ListNode head = new ListNode(-1);
ListNode next_p = head;
//数据全部存入数组————排序————重新建list
List<Integer> nums = new ArrayList<>();
for (int i = 0; i<lists.length;i++){
ListNode p = lists[i];
while(p!=null){
nums.add(p.val);
p=p.next;
}
}
Collections.sort(nums);
while(!nums.isEmpty()){
next_p.next = new ListNode(nums.remove(0));
next_p = next_p.next;
}
return head.next;
}
执行用时 :
14 ms, 在所有 Java 提交中击败了32.21%的用户
内存消耗 :
41.2 MB, 在所有 Java 提交中击败了71.61%的用户
思路二:将数组中list两两合并
两个链表的归并很容易完成。所以可以将list数组两个list进行合并,直到所有的list被合并完成。
时间复杂度:
merge的时间复杂度为O(n),n为两个链表的总长度
在此算法中
list0 list1 list2 list3 … listk-1
每两次归并实际上是前面所有的list的一次归并
list0 list1 l归并一次
list0 list1 list2 归并一次
list0 list1 list2 list3 归并一次
…
list0 list1 list2 list3…listk-1归并一次
所以
list0 遍历k-1次(k为list集合长度)
list1 遍历k-2次
listn遍历1一次
假设所有list中元素总个数为N,平均每个list中个数N/k,则其复杂度为:
(k-1)N/k+(k-2)N/k+…1N/k=((k*k-1)/2)*N/k=(N(k-1))/2
所以其时间复杂度为:O(kN)
public ListNode mergeKLists(ListNode[] lists) {
ListNode list = null;
//list数组为空返回null
if(lists.length==0 ) return list;
//归并返回ListNode
int i = 0;
//将i和i+1的list归并,归并后存入i+1的位置用于下次继续合并
for( ;i<lists.length-1;i++){
lists[i+1]=merge(lists[i],lists[i+1]);
}
return lists[i];
}
//merge
private ListNode merge(ListNode list, ListNode list1) {
ListNode re_list = new ListNode(-1);
if(list==null&&list1==null)
return re_list.next;
ListNode p = re_list;
while(list!=null&&list1!=null){
if(list.val<list1.val){
p.next = list;
list = list.next;
}else{
p.next = list1;
list1 = list1.next;
}
p=p.next;
}
if(list==null&&list1!=null)
p.next = list1;
if(list!=null&&list1==null)
p.next = list;
return re_list.next;
}
执行用时 :
143 ms, 在所有 Java 提交中击败了21.21%的用户
内存消耗 :
41.4 MB, 在所有 Java 提交中击败了67.22%的用户
思路三:利用优先队列
1.将所有数值存入优先队列(比较器ListNode val值递增)
2.将优先队列输出,重新连接
构造器比较排序时间复杂度O(logk),k为list个数
所以总的时间复杂度O(nlogk)
public ListNode mergeKLists(ListNode[] lists) {
ListNode head = null;
if(lists.length==0) return null;
//先将所有的数据存入优先队列中
Queue<ListNode> pq = new PriorityQueue<ListNode>(new Comparator<>() {
@Override
public int compare(ListNode listNode, ListNode t1) {
return listNode.val-t1.val;
}
});
for(int i = 0; i < lists.length; i++){
ListNode p = lists[i];
while(p!=null){
pq.offer(p);
p=p.next;
}
}
ListNode dummyhead = new ListNode(-1);
head = dummyhead;
while(!pq.isEmpty()){
head.next = pq.poll();
head = head.next;
}
head.next = null;//因为head不为空,所以head.next不可能出现空指针议程
return dummyhead.next;
}
执行用时 :
9 ms, 在所有 Java 提交中击败了42.66%的用户
内存消耗 :
41.6 MB, 在所有 Java 提交中击败了63.32%的用户
可以看出有时候暴力解决问题,还是很好地嘛~