跟着小蛋刷算法系列:合并两个有序链表 LeetCode 21题

题目描述

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

image-20211109212059350

解题思路

按照题目的描述,给定的两个链表各自本身是有序的,我们要将它合并成一个有序链表,也就是要把这两个链表合到一起重新排序得到最终的结果,这道题我提供了两种解法,非递归解法和递归解法。

非递归解法

首先自己实现一个单链表ListNode

public class ListNode<E> {
    public ListNode next;
    public E e;
​
    public ListNode(E e) {
        this(e, null);
    }
​
    public ListNode(E e, ListNode next) {
        this.e = e;
        this.next = next;
    }
​
​
    public ListNode() {
        this(null, null);
    }
复制代码

非递归代码

 
​
public class MergeList {
    public static void main(String[] args) {
        MergeList mergeList = new MergeList();
        ListNode node1 = new ListNode(1);
        ListNode node11 = new ListNode(3);
        node1.next = node11;
        node11.next = node111;
        ListNode node2 = new ListNode(2);
        ListNode node22 = new ListNode(4);
        ListNode node222 = new ListNode(6);
        node2.next = node22;
        node22.next = node222;
        ListNode node = mergeList.mergeTwoLists(node1, node2);
    }
​
    public ListNode mergeTwoLists(ListNode<Integer> l1, ListNode<Integer> l2) {
        //设置虚拟头节点
        ListNode<Integer> dummy = new ListNode<Integer>(0), pp = dummy;
        while (l1 != null && l2 != null) {
            if (l1.e <= l2.e) {
                pp.next = l1;
                pp = pp.next;
                l1 = l1.next;
            } else {
                pp.next = l2;
                pp = pp.next;
                l2 = l2.next;
            }
        }
        if (l1 != null) {
            pp.next = l1;
        }
        if (l2 != null) {
            pp.next = l2;
        }
        return dummy.next;
    }
}
​
复制代码

非递归解法总体思路就是通过 while 循环对 l1 和 l2 链表的值进行一一比较,假设第一轮比较 l1 的值是最小的,就将 l1 放入结果链表中,然后对 l1 进行移动到下一位,两者继续比较,依次类推,直到 l1 , l2 其中一方为空,跳出循环,跳出循环后因为两者肯定有一方还有值,直接将当前结果链表节点的next节点指向不为空的链表,最终就得到了排序后的结果,进行返回。

通过动态图来进行演示: [animate output image]

递归解法

   /**
     * 递归解法
     *
     * @param l1
     * @param l2
     * @return
     */
    public ListNode mergeTwoLists(ListNode<Integer> l1, ListNode<Integer> l2) {
        if (l1 == null) {
            return l2;
        } else if (l2 == null) {
            return l1;
        } else if (l1.e < l2.e) {
            l1.next = mergeTwoList(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoList(l1, l2.next);
            return l2;
        }
​
    }
复制代码

递归的终止条件就是两个链表 l1 , l2 其中有一个为空时就终止然后往上回溯,最终返回答案。

那怎么去递归的呢?

我们判断 l1 和 l2 头节点哪个更小,然后将较小节点的 next 指针指向其余节点的合并结果,就是继续调用递归方法,直到满足终止条件。

递归代码非常简洁,但是可能看起来很绕,一层一层的递归,一不小心就容易绕晕了,我们来把这个递归进行解剖,一步一步进行分析:

l1是1->3, l2 是 2->4->6

第一次方法调用

l1 的头节点是1,l2 是3,所以会将 l1.next 指向下次的方法调用

此时:结果链表为:l1->l1.next=mergeTwoList

  l1.next = mergeTwoList(l1.next, l2);
            return l1;
复制代码

第二次方法调用:

将l1的第二个节点和l2的头节点进行比较

3比2大,所以l1.next就是 l2 的头节点

 l2.next = mergeTwoList(l1, l2.next);
            return l2;
复制代码

结果链表: 继续顺延为:l1 -> l2-> l2.next=mergeTwoList

第三次方法调用: 就是 l2 的第二个节点和 l1 的第二个节点进行比较:

4比3大,因此l2的next节点就要l1的第二个节点

结果链表: l1-> l2->l1.next->l1.next.next = mergeTwoList

第四次方法调用:

此时l1没有第三个节点,所以l1当前节点为空,开始往上回溯:

因此 l1的空节点 就是l2的第二个节点

结果链表: l1-> l2 -> l1.next -> l2.next

那么最终结果值就为:1->2->3->4->6

总结

两种解法,可能第二种递归解法比较难以理解,我的建议是一步一步debug,看下值的变化,一步一步跟踪,就能彻底理解了,在这里给大家出个思考题,如果是多个有序链表进行合并,我们应该怎么操作呢?关注我,下篇文章为你解答。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员蛋蛋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值