23、 合并K个升序链表(分治+链表)

输入:lists = [ [1,4,5], [1,3,4], [2,6] ],让你设计一个程序将链表数组里面的所有链表按照升序的条件合并成一个链表。输出:[ 1,1,2,3,4,4,5,6]

这道题是一个困难级别的题目,之前遇到困难题目都是直接跳过,这次主要时看到题目和链表有关,决定试着做一下。

首先看到题目想到的时链表合并的算法,但是链表合并适用于两个链表,而题目要求是合并k个链表,所以第一感觉肯定是需要用到一个常用算法来帮助完成这道题目。这也是题目难度为困难的原因吧。即考察链表数据结构和链表合并操作,又要考察一个算法。然后我就开始寻思是什么算法。突然灵光一现,想到数组排序用的是分治,题目和数组排序还真有一点相似之处,所以尝试用分治法来求解。

分治法是将规模较大的问题分解成规模较小的问题进而求解,打眼一看题目,那么就把链表数组先分解成[1,4,5],[1,3,4]和[2,6]这两部分,然后再把左边分成[1,4,5]和[1,3,4]两部分,由于这两部分不能再分解,所以合并这两个链表,刚好链表的合并操作也是用于两个链表的。看似问题迎刃而解,但是上手编程还是编写不出来。

当时的想法是按照分治法的方法对数组进行划分,left=0,right=length-1,mid=(left+right)/2。直到不能再分解为止,就该对链表两个链表进行合并,这个时候我反而不知道合并函数的参数是什么,不知道参数到底是两个链表还是划分出来的序号。按道理说应该是两个链表,但是分治的时候怎么得到链表呢?最重要的一个问题是,如果合并完两个链表,那么链表数组的长度就会减一,那么原本分解好的数组就全乱了,原本得到的序号都成无效的了,那该怎么办。这是我当时最纠结的一个问题。

把自己编写代码时犹豫的问题记录下来:1、分治函数的参数怎么写,是两个listnode还是一个listnode[]数组。答:分治函数参数写listnode[]数组,合并链表的函数参数写两个listnode。2、分治时两个链表合并成一个链表会使得数组中链表数量减少,那么left和right怎么指示链表?答:合并两个链表,链表数组中的链表个数并不减少。

这里解释一下为什么两个链表合并之后数组的元素不会减一。因为数组减一只有在这种情况下才会发生,即将两个链表从数组中拿出来,合并完之后将这个新的链表放回数组中。也就是lists[0] lists[1]合并为lists[0]。然而在合并的时候并没有发生数组元素位置的移动。实际上两个链表的合并也仅仅是结点指针的变动。lists[0]是第一个链表的头结点,lists[1]是第二个链表的头结点。由于合并,lists[0]这个链表加入了lists[1]链表中的结点,lists[1]这个结点已经不是第二个链表的头结点了,原来那个第二个链表已经不存在了,被合并成一个链表了即lists[0],但lists[1]作为结点还存在数组中,只不过它现在表示的链表跟原来的链表不一样了,它表示的链表成了lists[1]链表的一部分。所以总的来说数组的长度并没有改变。其实也可以这么理解:链表数组中放的仅仅是每个链表的头结点,合并后头结点是没有变化的,即他们的个数没有少,但是它们所表示的链表却发生了变化。第一个头结点表示合并后的新链表,第二个头结点表示新链表中的一部分,因为第二个结点在合并的时候作为一个结点被加入到新链表中,由于它后面面链有结点,所以它所表示的就是新链表中的一个子链表,但实际上它已经没有意义了。

分治法代码如下:

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        int length = lists.length;
        if ( length == 0 || lists == null ) return null;
        int left = 0; int right = length - 1;
        return divide(lists, left, right);
    }
    public ListNode divide(ListNode[] lists, int left, int right) {
        if ( left == right ) return lists[left];
        ListNode merge = new ListNode();
        if ( left < right ) {
            int mid = (left+right)/2;
            ListNode l1 = divide(lists, left, mid);
            ListNode l2 = divide(lists, mid+1, right);
            merge = mergetwolinks(l1, l2);
        }
        return merge;
    }
    public ListNode mergetwolinks(ListNode l1, ListNode l2) {
        ListNode tou = new ListNode();
        ListNode cur = tou;
        while ( l1 != null && l2 != null ) {
            if ( l1.val <= l2.val ) {
                cur.next = l1;
                l1 = l1.next;
                cur = cur.next;
            }else {
                cur.next = l2;
                l2 = l2.next;
                cur = cur.next;
            }
        }
        cur.next = l1==null ? l2 : l1;
        return tou.next;
    }
}

 

你可能感兴趣的:(算法笔记,java,链表,算法,数据结构)