【LeetCode刷题记录3】23. 合并K个排序链表

个人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的排序实现
  • 学到了优先级队列的应用
  • 对链表类问题有了一些经验
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值