题目
将两个升序链表合并为一个新的升序链表并返回,新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
迭代法
迭代法是处理链表合并比较直观的解决方案,通过创建一个新的链表来存储合并后的结果。迭代法的基本思想是初始化一个哑节点作为新链表的头节点,然后遍历两个链表,每次从两个链表的头部取出值较小的节点添加到新链表的末尾,并移动对应链表的指针。当任一链表遍历完后,将另一链表剩余部分直接连接到新链表的末尾即可。使用迭代法求解本题的主要步骤如下。
1、创建一个哑节点dummy,用于方便操作新链表的头部。
2、初始化两个指针p1和p2,分别指向两个链表的头节点。
3、遍历两个链表,直到其中一个链表的指针达到末尾。在遍历过程中,比较p1和p2指向的节点值。将值较小的节点添加到dummy的末尾,并将对应指针向后移动。
4、当一个链表遍历完毕后,将另一个链表的剩余部分直接连接到新链表的末尾。
5、返回dummy.next作为新链表的头节点。
根据上面的算法步骤,我们可以得出下面的示例代码。
class ListNode:
def __init__(self, val = 0, next = None):
self.val = val
self.next = next
def merge_linked_list_iteration(l1: ListNode, l2: ListNode) -> ListNode:
# 创建一个哑节点
dummy = ListNode(0)
# 初始化当前节点指针
current = dummy
while l1 and l2:
if l1.val < l2.val:
current.next = l1
l1 = l1.next
else:
current.next = l2
l2 = l2.next
# 移动当前节点指针
current = current.next
# 处理剩余部分
if l1:
current.next = l1
elif l2:
current.next = l2
# 返回新链表的头节点
return dummy.next
def print_list(list: ListNode):
while list:
print(list.val, end = ' ')
list = list.next
print()
node3 = ListNode(4)
node2 = ListNode(2, node3)
node1 = ListNode(1, node2)
l1 = node1
node3 = ListNode(4)
node2 = ListNode(3, node3)
node1 = ListNode(1, node2)
l2 = node1
list_result = merge_linked_list_iteration(l1, l2)
print_list(list_result)
递归法
递归法利用了链表的自然递归结构,提供了一种更为优雅的解决方案。其基本思路是:如果一个链表为空,直接返回另一个链表;如果两个链表都不为空,则比较两个链表头节点的值,将值较小的节点作为新链表的头节点,然后递归地合并剩余的链表部分。通过这种方式,问题被不断分解,直到至少有一个链表为空,然后通过返回值逐步构建出合并后的新链表。使用递归法求解本题的主要步骤如下。
1、检查两个链表l1和l2是否为空。
(1)如果l1为空,直接返回l2,因为合并后的链表应包含l2的所有节点。
(2)如果l2为空,直接返回l1,因为合并后的链表应包含l1的所有节点。
2、如果两个链表都不为空,比较l1和l2的头节点值。
(1)将值较小的节点设为新链表的头节点。
(2)新链表的下一个节点将是递归调用的结果,参数为较小节点的下一个节点和另一个链表的头节点。
3、递归过程会不断重复上述步骤,直到某个链表为空。此时,递归结束,开始逐层返回,构建完整的合并链表。
根据上面的算法步骤,我们可以得出下面的示例代码。
def merge_linked_list_recursive(l1: ListNode, l2: ListNode) -> ListNode:
if not l1:
return l2
if not l2:
return l1
if l1.val < l2.val:
l1.next = merge_linked_list_recursive(l1.next, l2)
return l1
else:
l2.next = merge_linked_list_recursive(l1, l2.next)
return l2
node3 = ListNode(4)
node2 = ListNode(2, node3)
node1 = ListNode(1, node2)
l1 = node1
node3 = ListNode(4)
node2 = ListNode(3, node3)
node1 = ListNode(1, node2)
l2 = node1
list_result = merge_linked_list_recursive(l1, l2)
print_list(list_result)
总结
在处理链表问题时,迭代法通常较为直接且易于理解,特别是对于合并、排序等操作。通过引入哑节点,我们可以简化代码逻辑,避免对头节点的特殊处理。这种方法不仅适用于两个链表的合并,也容易扩展到多个链表的合并。迭代法的时间复杂度为O(m + n),其中m和n分别是两个链表的长度。这是因为,我们需要遍历两个链表的所有节点来完成合并操作。
递归法在处理链表问题时,提供了一种清晰且简洁的解决方案,尤其适合于像链表合并这样可以自然分解为相似子问题的任务。相比迭代法,递归法的时间复杂度也为O(m + n),但其减少了显式的循环和指针操作,使得代码更加直观。递归法的缺点是当链表非常长时,可能会导致调用栈溢出。