小木的算法日记——合成两个有序链表
接下来我们从本质上对这道题目进行一个拆解
输入: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
l1
和l2
均按 非递减顺序 排列
题目来源:力扣 21. 合并两个有序链表。
# 函数签名如下
def mergeTwoLists(l1: ListNode, l2: ListNode) -> ListNode:
解法
class Solution:def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:# 虚拟头结点dummy = ListNode(-1)p = dummyp1 = l1p2 = l2while p1 is not None and p2 is not None: # 比较 p1 和 p2 两个指针# 将值较小的的节点接到 p 指针if p1.val > p2.val:p.next = p2p2 = p2.nextelse:p.next = p1p1 = p1.next# p 指针不断前进p = p.nextif p1 is not None:p.next = p1if p2 is not None:p.next = p2return dummy.next
我们先讲解开头的两行代码
class Solution:def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:1 为什么我们在LEETCODE以及算法比赛中总会看待class Solution呢?
我们可以把其看作是答题卡证明,也就是class Solution:冒号之后的是答题卡区域,你需要把你的代码写到之后的区域,机器才能识别,面试官才能识别。但如果是在你自己的项目平时的草稿算法则不需要。class Solution 就是告诉你,这是考试,其之后的区域是答题卡在其中写代码才算数,才给分,其他区域不算数。2 为什么大多数时候leetcode会在开始的时候给一个函数呢?因为Leetcode要用统一的格式来自动测试你的代码。所以要统一函数,从而让机器自动批改。def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:self 代表一个实例对象也就是——我自己 你只要记住self代表我自己就欧克了。
何时会用到self??
在class类里面,函数的第一个参数必须是self。
类外面的话(或者没有类)则有或没有self都可以
LeetCode算法中的self是固定格式,你可以将其看做是占位符,不用特意处理。我自己class 游戏角色:def 自我介绍(self):print(f"我的名字是{self.名字},我的等级是{self.等级}")def 升级(self):self.等级 += 1 # 提升“我自己”的等级self就相当于我自己,存储着我的名字,等级等信息。——————通过 self.属性名 来读取或修改当前对象的属性。l1:ListNode 的意思是说明l1的类型是链表
-> ListNode: 代表最后输出的是一个链表
class Solution:def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:# 虚拟头结点dummy = ListNode(-1) 这里定义一个虚拟头节点,让其等于-1,这个值不重要,因为虚拟,所以值取几都欧克。重要的是它起到了一个暂时带头的作用(班级牌),从而简洁了代码,如果没有它,你还得判定到底谁做头。p = dummy 这里不是将dummy赋值给P 而是让p指针的位置移动到dummy节点上!!!p1 = l1 同理,让p1移动到l1链表的头部那里p2 = l2 同理while p1 is not None and p2 is not None: # 比较 p1 和 p2 两个指针# 将值较小的的节点接到 p 指针if p1.val > p2.val: 当p1指针所指的节点对应的值大于p2时,让在虚拟节点的那个指针p与p2刚才对的那个节点链接,然后让p2指针移动到l2链表的下一个节点继续进行比较。p.next = p2p2 = p2.nextelse:p.next = p1p1 = p1.next前面几行代码,p.next=p2是挂车厢也就是把p2对应的节点先高到虚拟节点之后(虚拟系欸但那的右指针),然后p2往后移。而p指针是不动的只是挂了一个原先是l2的第一个车厢,之后的p=p.next(p指针(朝向上))才是让p指针移动的原因。# p 指针不断前进p = p.next 关于执行if/else执行完成后就会执行p=p.next,因为其缩进相同,所以肯定会一个接一个执行。而以下这两行代码与while缩进一样,意思是执行完while后,执行它们两个,注意while的条件是p1与p2同时存在,且不为空,如果p2的节点大于p1呢?两者的节点数不相等呢?那如何对剩余的元素排序?下面两行代码解决了这个问题。
如果p1还有,则放在p2if p1 is not None:p.next = p1if p2 is not None:p.next = p2你这里可能会问,p.next=p1不是只是把p1所对应的那个节点接入后面了,为什么没有p=p.next移动节点,从而继续接入呢?这里有一个小知识点,就是p.next如果在循环之外(while for)则其p1就可以代笔着整个链表,p.next=p1就直接链接整个链表了。return dummy.next
最后返回虚拟节点之后的内容就是重新排序之后的内容。
AI: