美文网首页
合并两个有序链表【递归、迭代】

合并两个有序链表【递归、迭代】

作者: 瑜小贤 | 来源:发表于2019-12-17 11:16 被阅读0次

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

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

方法一:递归

思路

我们可以递归地定义在两个链表上进行合并(merge)操作的结果,如下所示(在不考虑空列表的情况下):
\begin{cases} list1[0] + merge(list1[1:], list2) \text{ list1[0] < list2[0]}\\ list2[0] + merge(list1, list2[1:]) \text{ otherwise} \end{cases}

也就是说,我们取两个列表头部中较小的那个,然后再加上合并其余元素所得到的结果。

算法

我们直接对上述递归建模,首先考虑边界情况。 具体来说,如果 l1 或 l2 最初为 null,则不需要执行合并,我们只需返回非空列表。否则,我们需要确定 l1 和 l2 中哪个的头节点更小,并递归地处理该头节点的 next 值以得到下一次合并结果。 如果两个列表都以空结束,那么递归就会停止。

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        else if (l2 == null) {
            return l1;
        }
        else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        }
        else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}
复杂度分析
  • 时间复杂度:O(n + m)
    因为每次递归调用都会将指向 l1 或 l2 的指针递增一次(逐渐接近每个列表末尾的 null),所以每个列表中的每个元素都会对 mergeTwoLists 进行一次调用。 因此,时间复杂度与两个列表的大小之和是线性相关的。

  • 空间复杂度:O(n + m)
    一旦调用 mergetwolist,直到到达 l1 或 l2 的末尾时才会返回,因此 n + m 的栈将会消耗 O(n + m) 的空间。

方法二:迭代

思路

我们完全可以通过迭代实现相同的思想,假设 l1 完全小于 l2,并逐个处理元素,在 l1 的必要位置插入 l2 元素。

算法

首先,设置一个 prehead 节点(虚节点),这会帮助我们轻松地返回合并之后的列表的头节点。 我们还需要维护一个 prev 指针,它指向可能需要调整 next 指针的当前节点。 然后,执行以下操作,直到 l1 和 l2 中至少有一个指向 null:如果 l1 处的值小于或等于 l2 处的值,那么我们将 l1 连接到前一个节点,并递增 l1。 否则,我们对 l2 做同样的事情。 不管我们连接的是哪个列表,我们都会增加 prev,使它总是保持比我们的表头落后一步。

循环终止后,l1 和 l2 中最多有一个是非空的。 因此(因为输入列表是按有序的),如果其中一个列表是非空的,那么它包含的元素一定大于所有先前合并的元素。 这意味着我们可以直接将非空列表连接到已合并列表并返回它。

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        // maintain an unchanging reference to node ahead of the return node.
        ListNode prehead = new ListNode(-1);

        ListNode prev = prehead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }

        // exactly one of l1 and l2 can be non-null at this point, so connect
        // the non-null list to the end of the merged list.
        prev.next = l1 == null ? l2 : l1;

        return prehead.next;
    }
}
复杂度分析
  • 时间复杂度:O(n + m)
    在每次循环迭代中, l1 和 l2 其中之一会递增,所以 while 循环运行的迭代次数等于这两个列表的长度之和。 所有其他的工作都不变,所以整体的复杂性是线性的。

  • 空间复杂度:O(1)
    迭代方法只会分配几个指针,因此它的总体内存占用是恒定的。

相关文章

网友评论

      本文标题:合并两个有序链表【递归、迭代】

      本文链接:https://www.haomeiwen.com/subject/hzhsnctx.html