笨办法学 Python · 续 练习 14:双链表

练习 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 = value
        self.next = nxt
        self.prev = prev

    def __repr__(self):
        nval = self.next and self.next.value or None
        pval = self.prev and self.prev.value or None
        return f"[{self.value}, {repr(nval)}, {repr(pval)}]"

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

class DoubleLinkedList(object):

    def __init__(self):
        self.begin = None
        self.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,而无需任何参考。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容