练习 14:双链表

原文:Exercise 14: Double Linked Lists

译者:飞龙

协议:CC BY-NC-SA 4.0

自豪地采用谷歌翻译

以前的练习可能需要花一段时间才能完成,因为你必须弄清楚如何使单个链表工作。希望视频为你提供完成练习的足够信息,并向你展示如何审计代码。在本练习中,你将实现更好的链表DoubleLinkedList

SingleLinkedList中,你应该已经意识到,涉及列表末尾的任何操作,都必须遍历每个节点,直到到达末尾。SingleLinkedList仅仅对于列表前面是高效的,那里你可以轻松地更改next指针。shiftunshift操作非常快,但poppush的开销随链表增大而增大。你可以通过保留下一个元素到最后一个元素的引用来加速,但是如果要替换该元素,该怎么办?同样,你必须遍历所有的元素来找到这个元素。你可以通过细微变化来获得一些速度改进,但更好的解决方案是,修改结构,使其可以从任何位置工作。

DoubleLinkedListSingleLinkedList几乎一样,但它还有prev(上一个)链接,指向它前面的DoubleLinkedListNode。每个节点有一个额外的指针,使许多操作突然变得容易得多。你还可以在DoubleLinkedList中,轻易添加一个指向end的指针,所以你可以直接访问头部和尾部。这使得pushpop效率更加高效,因为你可以直接访问尾部,并使用node.prev指针获取上一个节点。

考虑到这些变化,我们的节点类看起来像这样:

class DoubleLinkedListNode(object):def __init__(self, value, nxt, prev):self.value = valueself.next = nxtself.prev = prevdef __repr__(self):nval = self.next and self.next.value or Nonepval = self.prev and self.prev.value or Nonereturn f"[{self.value}, {repr(nval)}, {repr(pval)}]"

所有添加的东西就是self.prev = prev,以及在__repr__中处理它。DoubleLinkedList类的实现使用SingleLinkedList的相同方式,除了你需要为链表末尾添加一个额外的变量。

class DoubleLinkedList(object):def __init__(self):self.begin = Noneself.end = None

引入不变条件

所有要实现的操作都一样,但是我们有一些额外的事情需要考虑:

def push(self, obj):"""将新的值附加到链表尾部。"""def pop(self):"""移除最后一个元素并返回它。"""def shift(self, obj):"""将新的值附加到链表头部。"""def unshift(self):"""移除第一个元素并返回它。"""def detach_node(self, node):"""你有时需要这个操作,但是多数都在 remove() 里面。它应该接受一个节点,将其从链表分离,无论节点是否在头部、尾部还是在中间。"""def remove(self, obj):"""寻找匹配的元素并从中移除。"""def first(self):"""返回第一个元素的*引用*,不要移除。"""def last(self):"""返回最后一个元素的*引用*,不要移除。"""def count(self):"""计算链表中的元素数量。"""def get(self, index):"""获取下标处的值。"""def dump(self, mark):"""转储链表内容的调试函数。"""

使用self.end指针,你现在必须在每个操作中处理更多的条件:

  • 是否有零个元素?那么self.beginself.end都需要是None
  • 如果有一个元素,那么self.beginself.end必须相等(指向同一个节点)。
  • 第一个节点的prev必须始终为None
  • 最后一个节点的next必须始终为None

这些事实必须在DoubleLinkedList的生命周期中维持,这使得它们成为“不变条件”或者只是“不变量”。不变量的想法是,无论如何,这些基础检查显示了结构正常工作。查看不变量的一种方法是,任何重复调用的测试或者assert调用可以移动进一个函数,叫做_invariant,它执行这些检查。然后,你可以在测试中或每个函数的开始和结束处调用此函数。这样做会减少你的缺陷率,因为你假设“不管我做什么,这些都是真的”。

不变量检查的唯一问题是它们的运行花费时间。如果每个函数调用也调用另一个函数两次,那么你就为每个函数增加了潜在的重要负担。如果你的_invariant函数也会导致成本增加,就变得更糟。想象一下,如果你添加了不变量:“所有节点都有一个nextprev,除了第一个和最后一个。这意味着每个函数调用都遍历列表两次。当你必须确保类一直有效时,这是值得的。如果不是,那就是一个问题。

在这本书中,你可以使用_invariant函数,但请记住,你不需要始终使用它们。寻找方法,只在测试套件或调试中激活它们,或者在初始开发过程中使用它们,这是有效使用它们的关键。我建议你只在函数顶部调用_invariant,或者只在测试套件中调用它们。这是一个很好的权衡。

挑战练习

在本练习中,你将实现DoubleLinkedList的操作,但此时你还将使用_invariant函数来检查每个操作之前和之后是否正常。最好的方法是,在每个函数的顶部调用_invariant,然后在测试套件中的关键点调用它。DoubleLinkedList的测试套件几乎是SingleLinkedList测试的复制粘贴副本,除了在关键点添加_invariant调用。

SingleLinkedList一样,你需要自己手动研究此数据结构。你应该在纸张上绘制节点结构,并手动执行某些操作。接下来,在dllist.py文件中手动实现DoubleLinkedListNode。之后,花费一两个 45 分钟的时间,来尝试黑掉一些操作来弄清楚。我推荐pushpop。之后,你可以观看视频以查看我的工作,以及如何组合使用我的代码的审计和_invariant函数,来检查我在做什么。

深入学习

与以前的练习一样,你要按照记忆再次实现此数据结构。把你所知道的东西放在一个房间里,你的笔记本电脑在另一个房间。你将要执行此操作,直到你可以按照记忆实现DoubleLinkedList,而无需任何参考。

笨办法学 Python · 续 练习 14:双链表相关推荐

  1. 笨办法学 Python · 续 中文版

    笨办法学 Python · 续 中文版 原书:Learn More Python 3 The Hard Way 译者:飞龙 自豪地采用谷歌翻译 在线阅读 PDF格式 EPUB格式 MOBI格式 代码仓 ...

  2. 笨办法学 Python · 续 练习 33:解析器

    练习 33:解析器 原文:Exercise 33: Parsers 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 想象一下,你将获得一个巨大的数字列表,你必须将其输入到电子表格 ...

  3. 笨办法学 Python · 续 练习 0:起步

    练习 0:起步 原文:Exercise 0: The Setup 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 你需要设置和配置一些工具来学习此书.有可能你已经有了很多这些东西 ...

  4. 笨办法学 Python · 续 第二部分:简单的黑魔法

    第二部分:简单的黑魔法 原文:Part II: Quick Hacks 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 你有最好的想法,你会打动世界!你会成为一个亿万富豪!你的大 ...

  5. 笨办法学 Python · 续 练习 52:`moreweb`

    练习 52:moreweb 原文:Exercise 52: moreweb 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 现在,你已经使用 Python http.server ...

  6. 笨办法学 Python · 续 练习 24:URL 快速路由

    练习 24:URL 快速路由 原文:Exercise 24: Fast URL Search 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 我们将结束数据结构和算法的部分,并将 ...

  7. 笨办法学 Python · 续 练习 32:扫描器

    练习 32:扫描器 原文:Exercise 32: Scanners 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 我的第一本书在练习 48 中非常偶然涉及到了扫描器,但现在我 ...

  8. 笨办法学 Python · 续 第三部分:数据结构

    第三部分:数据结构 原文:Part III: Data Structures 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 你正在以你的方式构建个人流程,它让你以有限的阻碍快速 ...

  9. 笨办法学 Python · 续 练习 34:分析器

    练习 34:分析器 原文:Exercise 34: Analyzers 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 你现在有了一个解析器,它应该生成一个语法产生式对象树.我会 ...

最新文章

  1. 记 fastjson泛型转对象 第一次可以正常转,第二次就变成JSONArray 问题
  2. Linux防火墙与iptables命令
  3. linux su和sudo命令的区别(转)
  4. python导入其他py文件-Python如何import其它.py文件及其函数
  5. ifstat 命令查看linux网络I/O情况
  6. HDR:为用户打造的视觉盛宴
  7. libGDX-wiki发布
  8. JavaFX 2 GameTutorial第3部分
  9. 【踩坑】Linux下配置torch-geometric
  10. 机器学习实践指南(五)—— GD/SGD/MSGD 伪代码演示
  11. linux ipc 信号量,linux ipc信号量
  12. 展讯平台实现维吾尔语的几种方法
  13. 智能交通大数据体系实践
  14. 微星 B450M MORTAR 盲刷BIOS
  15. 5G无线技术基础自学系列 | 5G RAN网络架构关键技术
  16. 如何使用JMX_Expoter+Prometheus+Grafana监控Hadoop集群
  17. 精华【分布式微服务云架构dubbo+zookeeper+springmvc+mybatis+shiro+redis】分布式大型互联网企业架构!
  18. 英文金曲大赛c语言,英文歌曲_最激情!佐治亚理工开学典礼欢迎辞_沪江英语
  19. Ubuntu操作系统输入法键位错乱解法记录(输入法无法正确打出~、等字符)
  20. 藏宝阁游戏服务器维护中,梦幻西游2013年1月22日藏宝阁维护公告 17173.com网络游戏:《梦幻西游》专区...

热门文章

  1. (55)FPGA条件选择无优先级(if-else)
  2. FPGA跨时钟域处理方法延迟法
  3. 链接脚本文件(*.lds)
  4. 第二章16位和32位微处理器(2)——一些操作时序与中断
  5. 监控mysql的pr_zabbix之监控MySQL
  6. 力扣231.2的幂 C语言
  7. linux 权限管理命令
  8. java并发:初探sleep方法
  9. UI自动化测试POM设计之-maven工程
  10. grafana + influxdb + telegraf , 构建性能监控平台