问题:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

链表的定义如下:

public class ListNode {
      int val;
      ListNode next;
      ListNode() {}
      ListNode(int val) { this.val = val; }
      ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

Java 把多个列表合成一个 java合并多个链表_链表

问题分析:

我看到这个题目后首先想到了几个方法如下:

1)定义两个数组分别存储两个链表的值,再合并为一个新的数组,最后根据合并后的数组创建链表。

 这种方法的优点是数组能随时调用每一个值,而链表只能往后遍历,很容易想到这种方法。但是缺点也很明显,创建数组存储链表中的值耗费内存空间,且遍历两条链表后,还要遍历新数组,非常耗时。

2)定义一条新的链表,同时遍历两条链表并比较遍历到的结点的值,较小的值加入到新链表。

这种方法相较于第一种方法,内存空间占用小,且只遍历两条链表各一次,执行较快。但是由于链表结构的特殊性,需要考虑结点是否为null,其中一个链表已经遍历完毕的判断及处理,实现较第一种方法更难一些。值得注意的是:这里创建一个新的链表,原链表的的结点并没有改变。

3)上面两种方法是我看到题目就能立马想到的方法。第一种方法容易实现但是耗时长,内存占用多。第二种虽然耗时较短,但是新链表合并了两个链表内存占用还是不小的。

于是经过我仔细思考我得到了第三种方法。

由于并没有要求不能改变原链表结点,所有可以将其中一条链表插入另一条完成合并。我采用的就是这种方式。

具体解决思路如下:

1)链表问题先判断空链表的情况
2)根据两个链表首个结点的值的大小,决定哪个为返回链表,哪个为插入链表
          具体规则:
                  如果list1.val <= list2.val list1为返回链表,list2为插入链表
3)链表的插入规则:
          l1:返回链表正在遍历的结点
          l2:插入链表正在遍历的结点
          1、如果l1.next为null,则此时l1遍历到返回链表的尾部,那么l2及剩余结点的值一定大于l1,此时指向l2再放回链表就可以了。
          2、如果l2.val介于l1与l1.next之间,获取l2的值创建新结点,再插入新结点到l1
          3、如果l2中不能插入,即不满足上述条件,则l1往后继续遍历

 

Java 把多个列表合成一个 java合并多个链表_数据结构_02

具体的代码实现:

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {

        //链表为空的判断
        if(list1 == null && list2 == null){
            return null;
        }else if(list1 == null){
            return list2;
        }else if(list2 == null){
            return list1;
        }


        //用于指向list1中正在遍历的结点
        ListNode l1 = null;
        //用于指向list2中正在遍历的结点
        ListNode l2 = null;
        //用于指向返回链表
        ListNode l = new ListNode();

        /*
            根据首个元素值的大小决定哪个链表作为返回链表,哪个作为插入链表
            例如:
            list1.val <= list2.val
            则将list2作为插入链表,将list2中的结点插入到list1中
         */
        if(list1.val <= list2.val){
            l1 = list1;
            l2 = list2;
            l.next = list1;
        }else{
            l1 = list2;
            l2 = list1;
            l.next = list2;
        }

        //l2为插入链表,则只要l2没有结点则循环结束
        while(l2 != null){

            if(l1.next == null){//如果遍历到了l1尾部,那么l2剩余的结点元素一定比l1尾部的值大,
                l1.next = l2;
                return l.next;
            }else if(l2.val >= l1.val && l2.val < l1.next.val){//如果l2的元素值介于l1的两个结点中间,则插入该结点
                ListNode tmp = l1.next;
                l1.next = new ListNode(l2.val);//创建一个值与l2值相同的结点,再插入到l1
                l1.next.next = tmp;

                l1 = l1.next;
                l2 = l2.next;
            }else{//无插入操作则后移
                l1 = l1.next;
            }
        }
        return l.next;
    }

备注:我实现时是取出插入链表的值再插入到返回链表,仍然占用了一部分内存。如果想继续优化则需要用临时结点,直接插入会找不到返回链表的剩余结点。