java算法系列,第十一篇:合并有序链表

现在有两个链表:

[1,3,8,9],

[2,4]

要求合并成一个新的有序列表:[1,2,3,4,8,9]

这该怎么办呢?

方法一:

  1. 循环比较两个链表中的值,谁小就在该次循环中取它的节点添加到一个新链表去
  2. 循环完了以后将剩余的节点追加到新链表

 /**
     * 合并两个有序列表
     * @param p1 有序列表1
     * @param p2 有序列表2
     * @return   合并后的有序列表
     */
    public ListNode mergeTwoLists(ListNode p1, ListNode p2) {
        ListNode s = new ListNode(-1,null);//头哨兵
        ListNode p = s;
        while (p1 != null && p2 != null)
        {
            if(p1.getVal() < p2.getVal())
            {
                p.setNext(p1);
                p1 = p1.getNext();
            }
            else
            {
                p.setNext(p2);
                p2 = p2.getNext();
            }
            p = p.getNext();//更新p
        }
        if(p1 != null)//如果p1还不为空,就把p1剩下的追加到p后面
        {
            p.setNext(p1);
        }
        else if(p2 != null)//或者如果p2还不为空,就把p2剩下的追加到p后面
        {
            p.setNext(p2);
        }
       return s.getNext();
    }

方法二:

将方法一中用循环做的事改成递归来完成:

 /**
     * 合并两个有序列表
     * @param p1 有序列表1
     * @param p2 有序列表2
     * @return   合并后的有序列表
     */
    public ListNode mergeTwoLists2(ListNode p1, ListNode p2) {
       if(p1 == null)
       {
           return p2;
       }
       else if (p2 == null)
       {
           return p1;
       }
        if(p1.getVal() < p2.getVal())
       {
           p1.setNext(mergeTwoLists2(p1.getNext(),p2)); //让p1后面的节点去合并
           return p1;
       }
       else
       {
           p2.setNext(mergeTwoLists2(p1,p2.getNext()));//让p2后面的节点去合并
           return p2;
       }
    }

这种方式的话就不需要另外再新创建一个新链表,直接拿原来节点去组合。

以上两种方法都是合并两个有序链表,如果是多个呢?

这个就先用递归先合并成两个链表,然后再调用上面的合并两个链表的方法去合并就行了。

public ListNode mergeTwoLists3(ListNode[] nodes) {
       if (nodes.length == 0)//如果数组为空,就返回null
       {
           return null;
       }
       else
       {
           return split(nodes,0,nodes.length-1);//如果数组不为空,就把数组分成两半,然后合并
       }
    }
      /**
       * 分割数组,并合并成两个链表
       * @param nodes 有序列表数组
       * @param start 开始位置
       * @param end   结束位置
       * @return   合并后的有序列表
       */
    private ListNode split(ListNode[] nodes,int start,int end)
    {
        if (start == end)//如果数组只有一个元素,就返回这个元素
        {
            return nodes[start];
        }
        else if (start < end)//如果数组有多个元素,就把数组分成两半,然后合并
        {
            int mid = (start+end) >>> 1;
            ListNode left = split(nodes,start,mid);
            ListNode right = split(nodes,mid+1,end);
            return mergeTwoLists(left,right);//合并两个有序列表
        }
        else
        {
            return null;
        }
    }