天天AC系列(九):合并K个排序链表

1 题目

LeetCode第23题,合并k个有序的链表.
在这里插入图片描述java

2 暴力法

直接遍历全部链表,取出全部节点的值,用数组存储,非降序排序,而后建立一个新链表用头插法依次插入节点.git

List<Integer> s = new ArrayList<>();
for(ListNode x:lists)
{
    while(x != null)
    {
        s.add(x.val);
        x = x.next;
    }
}
s.sort((a,b) -> {return a.compareTo(b);});
ListNode result = new ListNode(0);
ListNode t = result;
for(Integer x:s)
{
    t.next = new ListNode(x);
    t = t.next;
}
return result.next;

在这里插入图片描述
这里要注意一下,sort那里不能写成:github

s.sort((a,b)->{return a>b ? 1 : -1;});

没有考虑到等于的状况,因此用compareTo代替:数组

s.sort((a,b)->{return a.compareTo(b);});

3 直接合并法

每次遍历全部链表,取出首节点的值,各个比较而后得出最小值,将最小值插入新链表,而后移动最小值所在的链表的指针,直到全部链表为空.ide

ListNode result = new ListNode(0);
ListNode t = result;
int len = lists.length;
int nullNodeNums = 0;
for(boolean [] b = new boolean[len];nullNodeNums<len;)
{
    int min = Integer.MAX_VALUE;
    int minIndex = -1;
    for(int index = 0;index<len;++index)
    {
        ListNode x = lists[index];
        if(x == null)
        {
            if(!b[index])
            {
                b[index] = true;
                ++nullNodeNums;
            }
        }
        else if(x.val < min)
        {
            min = x.val;
            minIndex = index;
        }
    }
    if(minIndex != -1)
    {
        t.next = new ListNode(min);
        t = t.next;
        lists[minIndex] = lists[minIndex].next;
    }
}
return result.next;

这里使用了一个布尔数组判断是否某个节点已经移动到尾部,即表示是否为空,为空的话跳过这个节点,不为空的话取其值,计算是否为最小值.获得最小值后,添加到结果节点中,并移动最小值所在链表的指针.
在这里插入图片描述
这个方法看起来慢得很啊.指针

4 优先队列

优先队列是上两个方法的结合,遍历全部节点,取值并根据其值肯定优先级添加到优先队列中,而后依次出队,将出队的值直接插入到新链表中.code

PriorityQueue<Integer> queue = new PriorityQueue<>();
for(ListNode x:lists)
{
    while(x != null)
    {
        queue.add(x.val);
        x = x.next;
    }
}
ListNode s = new ListNode(0);
ListNode t = s;
while(!queue.isEmpty())
{
    t.next = new ListNode(queue.poll());
    t = t.next;
}
return s.next;

java的优先队列能够直接add便可,按照默认出队序列(对于整数是小的先出)使用尾插法插入到新链表中.
在这里插入图片描述
嗯,好像还能够的样子,可是仍是不够快.blog

5 两两合并法

合并k个链表,至关于合并2个链表k-1次,利用递归的思想,每次合并两个链表,将合并后的链表后返回做为下一个要合并的链表继续合并.排序

public ListNode mergeKLists(ListNode[] lists) {
    if (lists == null || lists.length == 0)
        return null;
    ListNode t = lists[0];
    for(int i=1;i<lists.length;++i)
        t = merge(t, lists[i]);
    return t;
}
//public ListNode merge(ListNode a,ListNode b)

merge为直接合并两个链表的操做,不难,就不贴代码了,首先赋值t为第一个链表,而后依次合并t与剩下的n-1个链表.
在这里插入图片描述
好慢啊.递归

6 分治法

分治法是两两合并法的改进,两两合并每次合并两个链表,分治法每次合并一半数量的链表,整体思想是这样的:想要获得最终有序的链表,若左半部分的链表与右半部分的链表都有序,则至关于合并两个有序链表,为了获得左半部分的有序链表,须要继续对左半部分进行一半的分割,再次分红左半部分与右半部分,而后再分,直到某部分只有一个链表,而后返回,以合并两个普通有序链表的方式合并两个返回的链表.

public ListNode f(int start,int end)
{
    int len = end - start;
    if(len <= 1)
        return lists[start];
    ListNode l = f(start,start+len/2);
    ListNode r = f(start+len/2,end);
    return merge(l, r);
}

代码很是简洁,一开始为判断递归的条件,区间长度小于等于1直接返回[start]的节点,而后递归合并左半部分与右半部分的节点.
在这里插入图片描述
一个字,舒服.
真快.

7 源码

github

码云

相关文章
相关标签/搜索