个人LeetCode刷题记录:23. 合并K个排序链表
题目地址
法一:转成动态数组
遍历所有链表,把结点放入一个动态数组,对动态数组排序,然后再把动态数组转成链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution
{
public ListNode mergeKLists(ListNode[] lists)
{
//自定义Comparator对象,自定义排序方法
Comparator c = new Comparator<Integer>()
{
@Override
public int compare(Integer o1, Integer o2)
{
if((int)o1<(int)o2) return -1;
else if((int)o1==(int)o2) return 0;
else return 1;
}
};
List<Integer> list = new ArrayList<>(); //创建动态数组
for (ListNode node : lists)
{
for(ListNode p = node; p!=null; p = p.next)
{
list.add(p.val);
}
}
list.sort(c); //动态数组排序
ListNode dummy = new ListNode(0); //创建哑结点,使所有节点都有前驱节点
ListNode tmp = dummy;
for(Integer i : list )
{
tmp.next = new ListNode(i);
tmp = tmp.next;
}
return dummy.next;
}
}
法二:优先级队列
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists == null || lists.length == 0) return null;
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 0;
else return 1;
}
});
for(ListNode node: lists)
{
for(ListNode p = node; p!=null; p=p.next ) queue.add(p);
}
ListNode res = new ListNode(0);
ListNode tmp = res ;
while(!queue.isEmpty()){
tmp.next = queue.remove();
tmp = tmp.next;
}
tmp.next = null; //!非常重要!防止出现循环链
return res.next;
}
}
系统是直接遍历链表的方式确认结果的,队列的节点还保持者原本的节点关系属性next,大部分next值可以被覆盖,但最后一个节点可能还连着前面的节点,形成循环链,比如:
a.val = b.val
a.next = b
b.next = null
最后b先加在结果链上,然后a连在b上(b.next = a),但是a还保持着a.next = b,就形成了循环,导致超出内存限制
有一部分测试正常通过的原因:每个链表上最后一个节点值和之前的节点值不重复的情况,最后结果链上最后一个节点肯定指向null,也就是[…->a->b]不会出问题,而[…>a->a]这种有可能形成循环链
- 其实有不用拆成一个个单独节点、还能避免循环的方法:把头节点值最小的子链remove()出来,然后把把头节点接在结果链,然后再把子链放回去。 代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution
{
public ListNode mergeKLists(ListNode[] lists)
{
if(lists == null || lists.length == 0) return null;
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 0;
else return 1;
}
});
for(ListNode node: lists) if(node != null) queue.add(node);
ListNode dummy = new ListNode(0);
ListNode tmp = dummy ;
while(!queue.isEmpty())
{
tmp.next = queue.remove();
tmp = tmp.next;
if(tmp.next != null) queue.add(tmp.next);
}
//tmp.next = null; //每次都取一个子链的首节点,最后的节点一定指向null,不用担心循环链出现
return dummy.next;
}
}
法三:分治法
前置问题:21.两个有序链表合并
- n个链表合并转成n-1次两两合并,代码如下
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution
{
public ListNode merge2Lists(ListNode list1, ListNode list2)
{
ListNode dummy = new ListNode(0);
ListNode tmp = dummy;
ListNode p1=list1;
ListNode p2=list2;
while( p1 != null && p2 != null )
{
if(p1.val < p2.val)
{
tmp.next = p1;
tmp = tmp.next;
p1 = p1.next;
}
else
{
tmp.next = p2;
tmp = tmp.next;
p2 = p2.next;
}
}
if( p1 != null ) tmp.next = p1;
if( p2 != null ) tmp.next = p2;
return dummy.next;
}
public ListNode mergeKLists(ListNode[] lists)
{
if(lists == null || lists.length == 0) return null;
ListNode res = lists[0];
for(int i=1; i< lists.length; i++)
{
res = merge2Lists(res, lists[i]);
}
return res;
}
}
- 再进一步,引入分治的思想,两两配对,1与n合并,2与n-1合并,i与n-i合并,不断重复上述过程,最后剩下一条链
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution
{
public ListNode merge2Lists(ListNode list1, ListNode list2)
{
ListNode dummy = new ListNode(0);
ListNode tmp = dummy;
ListNode p1=list1;
ListNode p2=list2;
while( p1 != null && p2 != null )
{
if(p1.val < p2.val)
{
tmp.next = p1;
tmp = tmp.next;
p1 = p1.next;
}
else
{
tmp.next = p2;
tmp = tmp.next;
p2 = p2.next;
}
}
if( p1 != null ) tmp.next = p1;
if( p2 != null ) tmp.next = p2;
return dummy.next;
}
public ListNode mergeKLists(ListNode[] lists)
{
if(lists == null || lists.length == 0) return null;
int right = lists.length ;
int mid = right / 2;
while(right>1)
{
for(int i = 0; i <mid; i++) lists[i] = merge2Lists(lists[i], lists[right-1-i]);
right = (right+1)/2;
mid = right / 2;
}
return lists[0];
}
}
right,mid 的更新还不是很明白
需要补张分治法的图
总结
- 学到了ArrayList的排序实现
- 学到了优先级队列的应用
- 对链表类问题有了一些经验