剑指 Offer 25. 合并两个排序的链表

114 阅读2分钟

前言:剑指offer刷题系列

问题:

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

递归版思路:

首先,代码检查 l1 和 l2 是否为 null。如果其中一个为 null,则返回另一个链表。

接着,代码创建了一个新的链表节点 node,并根据 l1.val 和 l2.val 的大小关系设置它的值。如果 l1.val <= l2.val,则将 node.val 设置为 l1.val,并递归调用 mergeTwoLists(l1.next,l2) 函数,将返回值赋给node.next。否则,将 node.val 设置为 l2.val,并递归调用 mergeTwoLists(l1,l2.next) 函数,将返回值赋给 node.next。

最后,返回新创建的节点 node。

基于上述思考,代码如下:

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var mergeTwoLists = function(l1, l2) {
    if(l1 == null) return l2
    if(l2 == null) return l1
    let node = new ListNode(0)
    if(l1.val <= l2.val){
            node.val = l1.val
            node.next = mergeTwoLists(l1.next,l2)
        }else{
            node.val = l2.val
            node.next = mergeTwoLists(l1,l2.next)
        }
    return node
};

执行结果如下图:

image-20230714143153338.png

非递归版思路:

首先,代码创建了一个新的空节点list,并创建一个指针 pre 指向它。

接着,代码使用一个 while 循环遍历两个链表。在循环中,比较 l1 和 l2 指向的节点的值,将较小的值添加到新链表中,并将对应的指针向后移动一步。然后将 pre 指针向后移动一步。

当循环结束后,判断 l1 和 l2 中是否还有剩余元素。如果有,则将剩余元素全部添加到新链表中。

最后,返回新链表的头节点。

基于上述思考,代码如下:

var mergeTwoLists = function(l1, l2) {
    const list = new ListNode(null)
    let pre = list
    while(l1 && l2){
        if(l1.val < l2.val){
            pre.next = l1
            l1 = l1.next
        }else{
            pre.next = l2
            l2 = l2.next
        }
        pre = pre.next
    }
    pre.next = l1 ? l1 : l2
    return list.next
};

执行结果如下图:

image-20230714165523120.png

学到的知识点:

三元运算符的优先级高于赋值符号:非递归代码中的倒数第二句是pre.next = l1 ? l1 : l2。这句代码使用了三元运算符 ? : 来判断 l1 是否为真值。如果 l1 为真值,则将 pre.next 的值设置为 l1;否则,将 pre.next 的值设置为 l2。三目运算符的使用让代码更简洁,看起来更美观。

这句代码的作用是将未到达链表尾部的指针所在的链表中剩余的元素全部添加到新链表中。当循环结束后,l1 和 l2 中至少有一个指针已经到达了链表尾部。如果 l1 未到达链表尾部,则将剩余的元素全部添加到新链表中;否则,将 l` 中剩余的元素全部添加到新链表中。

这句代码的作用是将未到达链表尾部的指针所在的链表中剩余的元素全部添加到新链表中。它使用了三元运算符来简洁地实现这一功能。