leetcode
leetcode 1 ~ 50
合并两个有序链表

合并两个有序链表

难度:

标签:

题目描述

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

 

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

 

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1l2 均按 非递减顺序 排列

代码结果

运行时间: 48 ms, 内存: 17 MB


/*
 * 思路:
 * 1. 将链表转换为流,合并并排序。
 * 2. 将排序后的数据重新构造成链表。
 */
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
public class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        // 将两个链表转换为列表
        List<Integer> list1 = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        while (l1 != null) {
            list1.add(l1.val);
            l1 = l1.next;
        }
        while (l2 != null) {
            list2.add(l2.val);
            l2 = l2.next;
        }
        
        // 合并两个列表并排序
        List<Integer> sortedList = Stream.concat(list1.stream(), list2.stream())
                                        .sorted()
                                        .collect(Collectors.toList());
        
        // 将排序后的列表重新构造成链表
        ListNode dummy = new ListNode(0);
        ListNode current = dummy;
        for (int val : sortedList) {
            current.next = new ListNode(val);
            current = current.next;
        }
        
        return dummy.next;
    }
}

解释

方法:

该题解使用了双指针的方法来合并两个有序链表。具体思路如下: 1. 创建一个虚拟头节点 dummy,使用指针 p 指向 dummy。 2. 同时遍历两个链表 list1 和 list2,比较当前节点的值,将较小的节点接到 p 指针后面。 3. 移动对应链表的指针到下一个节点。 4. 移动 p 指针到新接入的节点。 5. 重复步骤 2-4,直到其中一个链表遍历完毕。 6. 将剩余未遍历完的链表接到 p 指针后面。 7. 返回 dummy 的下一个节点,即合并后的链表的头节点。

时间复杂度:

O(m+n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在处理剩余未遍历完的链表时,你选择直接创建新的ListNode而不是直接将剩余链表接在p指针后面?
在实际的LeetCode题解中,直接创建新的ListNode确实看起来是一种选择,但这种方式可能会导致混淆。理想的处理方法是直接将剩余的链表接到p指针后面,以避免额外的空间占用和不必要的节点复制。实际代码中应直接将剩余链表接到p后,例如:p.next = list1 或 p.next = list2,这样更加高效,因为不需要逐个节点复制剩余链表。
🦆
在对两个链表的节点值进行比较时,当值相等时选择将哪个链表的节点接到新链表后面有什么依据?
当两个链表的节点值相等时,选择哪个链表的节点接到新链表后面通常基于一致性和简洁性的考虑,通常没有固定规则。因为两个节点值相同,接入哪一个都不会影响最终链表的顺序性或正确性。示例中如果先接list1,就继续这一策略,保持代码的一致性。
🦆
这种双指针方法处理合并两个有序链表时,如果链表长度差异很大,是否会影响算法的效率?
双指针方法处理合并两个有序链表的效率主要取决于两个链表的总节点数,即时间复杂度为O(m+n),其中m和n分别是两个链表的长度。即使链表长度差异很大,算法的效率主要受较长链表的长度影响,但总体时间复杂度不会变。因此,长度差异虽存在,不会额外影响算法的效率。
🦆
在创建虚拟头节点 dummy 的目的是什么?在不使用虚拟头节点的情况下,这个算法应如何修改?
虚拟头节点 dummy 的主要目的是简化链表操作,避免处理头节点为空的特殊情况,使得链表合并逻辑更清晰、更统一。不使用虚拟头节点时,算法需要额外的条件判断来初始化合并后的链表头节点,并在每次插入时判断当前链表是否为空,增加代码复杂度。
🦆
题解中提到的空间复杂度是O(1),但是在合并过程中创建了新的ListNode实例。这是否意味着对原链表结构的修改?如果想保持原有链表结构不变,应该如何处理?
实际上,在合并链表时应避免创建新的ListNode实例,而是应该直接调整原有链表节点的指针,以保持空间复杂度为O(1)。这样不会修改原链表结构。如果要保持原链表结构不变,则需要创建新链表的每个节点的副本,这样会增加空间复杂度到O(m+n)。
🦆
返回的是 dummy 的下一个节点,这种处理方式是否有可能在某些情况下导致错误或是异常,例如当输入的两个链表都为空时?
返回dummy的下一个节点是一种常用的处理方式,以确保能正确返回合并后的头节点。当两个输入链表都为空时,dummy的下一个节点也会是空,这恰恰正确地表示了合并后链表也为空的情况,因此不会导致错误或异常。

相关问题

合并 K 个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

 

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

 

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4

合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 nums2,另有两个整数 mn ,分别表示 nums1nums2 中的元素数目。

请你 合并 nums2 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n

 

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

 

提示:

  • nums1.length == m + n
  • nums2.length == n
  • 0 <= m, n <= 200
  • 1 <= m + n <= 200
  • -109 <= nums1[i], nums2[j] <= 109

 

进阶:你可以设计实现一个时间复杂度为 O(m + n) 的算法解决此问题吗?

排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

 

示例 1:

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

输入:head = []
输出:[]

 

提示:

  • 链表中节点的数目在范围 [0, 5 * 104] 内
  • -105 <= Node.val <= 105

 

进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

最短单词距离 II

最短单词距离 II