LeetCode详解之如何一步步优化到最佳解法:21. 合并两个有序链表

 LeetCode详解系列的总目录(持续更新中):

LeetCode详解之如何一步步优化到最佳解法:前100题目录(更新中...)-CSDN博客

LeetCode详解系列的上一题链接:

LeetCode详解之如何一步步优化到最佳解法:20. 有效的括号-CSDN博客

目录

LeetCode详解系列的上一题链接:

21. 合并两个有序链表

解法1:暴力解法

解法1思路:

代码:

解法性能:

优化思路:

解法2:最终版

代码:

解法性能: 

解法分析:


21. 合并两个有序链表

本题题目链接:21. 合并两个有序链表 - 力扣(LeetCode)

解法1:暴力解法

解法1思路:

这道题的输入是两个升序的链表,期望的输出也是一个升序的链表。此外,我们看到输入的两个链表节点数都是[0, 50],因此链表有可能为空,需要特殊处理。

如果两个链表都是空链表,则返回list1(空链表)。否则,定义一个要输出的链表,并定义一个指针指向这个链表,这个指针是用来添加该输出链表的后续节点的(因为是单向的链表,只能从头往尾部遍历,而不能反过来,因此如果我们不使用一个额外的指针的话,可能会找不到输出链表的表头在哪里)。

接着,我们遍历输入的两个链表list1和list2直到其中一个链表遍历完毕。对于两个链表当前的节点,比较其数值val,数值小的那一个节点存入到输出链表中,并且对应的输入链表向后遍历一个节点。每一个循环下来,如果输入的两个链表还没有遍历完毕的话,在输出的链表中添加一个新的节点。

当至少其中一个输入链表遍历完毕后,判断哪一个链表已经遍历完毕,则将另一个链表的剩下部分接到输出链表中(因为剩下的链表自身是升序,且第一个节点的值是大于等于输出链表最后一个节点的值的,因此可以直接接到输出链表的最后)。最后,将输出链表返回。

代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        if not list1 and not list2:
            return list1

        result = ListNode()
        pt = result
        while list1 and list2: 
            if list1.val >= list2.val:
                pt.val = list2.val
                list2 = list2.next
            else:
                pt.val = list1.val
                list1 = list1.next
            
            if list1 or list2:
                pt.next = ListNode()
                pt = pt.next
        
        if list1:
            while list1:
                pt.val = list1.val
                list1 = list1.next
                if list1:
                    pt.next = ListNode()
                    pt = pt.next
        elif list2:
            while list2:
                pt.val = list2.val
                list2 = list2.next
                if list2:
                    pt.next = ListNode()
                    pt = pt.next

        return result

解法性能:

优化思路:

在上面的代码中,我们需要单独处理两个输入链表都是空链表的原因之一是创建一个新的ListNode节点的话,其val是会有默认值,这样即使我们没有更新输出的链表,其输出结果也是非空链的。但,如果我们在输出的时候,从输出链表的下一个节点开始算起,则即使两个输入链表都为空链表,我们的输出结果也是空链表。因此,我们最后的返回结果为”result.next”,我们就不需要单独在开头进行额外的处理。

此外,在循环中,对于数值比较小的节点,可以直接将这个节点添加到输出链表中,而不是像前面的代码一样,给val这个attribute赋值。然后,因为最终是输出”result.next”,所以在循环中,是给”pt.next”赋值,接着,pt指向它的next节点。

最后,在至少其中一个输入链表遍历完毕后,判断哪一个链表已经遍历完毕,可以直接添加到输出链表的最后,而无需像上面的代码一样再遍历一遍。

在优化完这些后,代码如下所示:

解法2:最终版

代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        result = ListNode(-1)
        pt = result
        while list1 and list2: 
            if list1.val >= list2.val:
                pt.next = list2
                list2 = list2.next
            else:
                pt.next = list1
                list1 = list1.next
            
            pt = pt.next
        
        if list1:
            pt.next = list1
        elif list2:
            pt.next = list2

        return result.next

解法性能:  

解法分析:

在优化后,因为少遍历一些循环以及判断,所以,所需要的执行时间减少了。此外,因为代码数量的减少,也减少了需要的内存。