天天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

碼雲

相關文章
相關標籤/搜索