前言
实测通义灵码:解锁智能编程的钥匙:https://developer.aliyun.com/article/1367211?spm=a2c6h.13148508.setting.15.5f074f0etrEHdr
探索通义灵码在算法生成中的无限潜力——数组篇:https://developer.aliyun.com/article/1367500?spm=a2c6h.13148508.setting.14.5f074f0etrEHdr
探索通义灵码在算法生成中的无限潜力——字符串篇:https://developer.aliyun.com/article/1368451?spm=a2c6h.13148508.setting.14.5a814f0eodKUMp
链表
链表(Linked List)是最简单的线性的、动态数据结构。理解它是理解树结构、图结构的基础。
区别于数组,链表中的元素不是存储在内存中连续的一片区域,链表中的数据存储在每一个称之为「结点」复合区域里,在每一个结点除了存储数据以外,还保存了到下一个节点的指针(Pointer)。
由于不必按顺序存储,链表在插入数据的时候可以达到 O(1) 的复杂度,但是查找一个节点或者访问特定编号的节点则需要 O(n) 的时间。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
在计算机科学中,链表作为一种基础的数据结构可以用来生成其它类型的数据结构。链表通常由一连串节点组成,每个节点包含任意的实例数据(data fields)和一或两个用来指向上一个/或下一个节点的位置的链接(links)。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的访问往往要在不同的排列顺序中转换。而链表是一种自我指示数据类型,因为它包含指向另一个相同类型的数据的指针(链接)。
链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。
链表通常可以衍生出循环链表,静态链表,双链表等。对于链表使用,灵活使用虚拟头结点可以简化问题。
删除链表中的节点
给出提问如下:
有一个单链表的 head,我们想删除它其中的一个节点 node。
给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。
链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。
删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:
给定节点的值不应该存在于链表中。
链表中的节点数应该减少 1。
node 前面的所有值顺序相同。
node 后面的所有值顺序相同。
请根据下述代码完成:
class Solution:
def deleteNode(self, node):
生成代码如下:
对照之后存在错误,根据报错信息,'ListNode' object has no attribute 'head',可以推测出ListNode类中没有定义head属性或方法。
于是开启我的调教大法,告知它存在这个问题,让它优化完善。
但是不巧的是,仍然存在报错,经过多次尝试后,发现均有不同程度报错,且即使多次调教和定向训练后仍然存在不同程度语法错误。
最后还是决定利用大模型的随机性,在经过 9 次 clear context(清楚记忆重新生成),终于给我生成出了一次正确答案。
给出代码及对照结果, 正确 !
但由于前两篇的经验,此处我开始尝试让通义灵码在保证正确性的前提下降低时间、空间复杂度。
果真有用!
删除链表的倒数第N个节点
给出提问如下:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。请结合以下代码来完成:
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
生成代码如下:
但是奇怪的是,此次代码结果竟然又是存在语法错误的!经过一些排查,其实我发现该代码逻辑似乎并无问题,只是通义灵码语义理解时出现了偏差!
根据题设是给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。(下方红框所示)
但是根据通义灵码的理解意思,它理解的头结点是链表的第一位。
此处也可以通过通义灵码自身的解释看出:
随后,我告知了它正确的示例:
给出代码及对照结果, 正确 !
反转链表
给出提问如下:
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。请根据下述代码来完成:
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
生成代码如下:
给出代码及对照结果, 正确 ,今天难得的一遍过!
时间、空间复杂度都比较可观了。
合并两个有序链表
给出提问如下:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 请根据下述代码完善:class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
生成代码如下:
【解释】:
首先判断两个链表是否为空,如果有一个为空,则返回非空的链表。
创建一个虚拟节点dummy和当前节点curr,用于拼接两个链表的结果。
使用循环遍历两个链表,每次比较两个链表当前位置节点的值,将较小的值拼接到新链表上,并将指针后移。
遍历完两个链表后,若某个链表还未遍历完,则将其剩余部分接在新链表的尾部。
返回虚拟节点dummy的下一个节点作为新链表的头节点。
给出代码及对照结果, 正确 !
看起来时间复杂度有些高了,于是我试着告诉它:
于是乎:
回文链表
给出提问如下:
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。请结合下述代码来完成:class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
生成代码如下:
给出代码及对照结果, 正确 !
时间、空间复杂度依旧很可观!
环形链表
给出提问如下:
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。请结合下述代码完成:class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
生成代码如下:
给出代码及对照结果, 正确 !
继续降低时间、空间复杂度。
效果太显著了!
结论
总体来看,通义灵码对于基础链表的处理还是很好的,但是根据今天的测试内容来看有两点是跟前几次不一样的:
在处理链表类型题目时较容易出现语法错误。比如常见的:缩进错误、缺少定义属性或方法等。(这两个较为常见)
对于链表代码问题的引导调优有些困难,说白了就是此时通义灵码似乎特别“固执”,尤其是语法问题的优化有些死板。
题目 | 最初答案 | 最终答案 | 执行用时 | 内存消耗 |
---|---|---|---|---|
删除链表中的节点 | × | √ | 超过75.88% | 超过92.07% |
删除链表的倒数第N个节点 | × | √ | 超过40.86% | 超过75.04% |
反转链表 | √ | √ | 超过76.14% | 超过99.42% |
合并两个有序链表 | √ | √ | 超过97.74% | 超过40.70% |
回文链表 | √ | √ | 超过87.80% | 超过72.56% |
环形链表 | √ | √ | 超过53.65% | 超过99.18% |
【注意】最初答案指第一次生成的答案是否正确;最终答案指多次调教之后生成的答案是否正确;执行用时越短,超过的比例就越多;内存消耗越少,超过的比例就越多。