核心:掌握如何连接两个有序链表
出错处:定义了新的链表,但是未想起来定义指针来为该链表添加节点
解决方法1-迭代法
对前序节点处理,之后处理剩余节点。
def mergeTwoLists(self, l1, l2):
p1 = l1
p2 = l2
dummy = ListNode(0) # Instantiate a new ListNode object as the head of the result list
cur = dummy # Use cur pointer to move and connect nodes
while p1 is not None and p2 is not None:
if p1.val > p2.val:
cur.next = p2
p2 = p2.next
else:
cur.next = p1
p1 = p1.next
cur = cur.next
if p1 is not None:
cur.next = p1
if p2 is not None:
cur.next = p2
return dummy.next # Return the next node of the head node, as the head node is empty
#Not using Pointers
def mergeTwoLists ( self , l1 , l2 )
dummy = ListNode(0)
cur = dummy
while l1 and l2:
if l1.val < l2.val:
cur .next = ListNode(l1.val)
l1 = l1.next
else:
cur . next = ListNode ( l2 . val )
l2 = l2.next
cur = cur .next
cur.next = l1 or l2
return dummy.next
解决方法2-递归法
def mergeTwoLists(self, l1, l2):
if not l1:
return l2
if not l2:
return l1
if l1.val < l2.val:
l1.next = self.mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2
复杂度
迭代方法:
- 时间复杂度:O(n),其中 n 是两个链表中节点个数的总和。在最坏情况下,我们需要遍历完整的两个链表来完成合并。
- 空间复杂度:O(1),迭代方法只需要常量级别的额外空间来存储一些指针变量,因此空间复杂度是常数级的。
递归方法:
- 时间复杂度:O(n),其中 n 是两个链表中节点个数的总和。在最坏情况下,递归方法需要遍历完整的两个链表来完成合并,因此时间复杂度与迭代方法相同。
- 空间复杂度:O(n),递归方法在执行过程中需要维护递归调用栈,其空间复杂度取决于递归调用的深度。在最坏情况下,如果链表较长,递归调用栈可能会达到 O(n) 的规模。
拓宽:虚拟头节点
当处理链表操作时,有时会使用虚拟头节点(dummy node)来简化操作。虚拟头节点是一个不存储实际数值的节点,它只是作为一个占位符存在,方便处理链表的插入和删除操作。
在合并两个有序链表、反转链表或者其他涉及头节点特殊处理的操作中,使用虚拟头节点可以大大简化代码逻辑,尤其是在涉及边界条件处理时。它可以有效避免单独处理头节点的情况,统一从虚拟头节点的下一个节点开始遍历整个链表。
在实际操作中,创建虚拟头节点的过程通常如下:
- 创建虚拟头节点,并将其初始化为空节点或者特定数值。
- 使用一个指针(通常命名为
cur
)指向虚拟头节点,用来进行链表操作。 - 最终返回虚拟头节点的下一个节点作为操作的结果。
使用虚拟头节点能够使链表操作更加简洁和统一,避免了对头节点特殊情况的处理,同时也提高了代码的可读性和易维护性。