轨道
/
Python
Python
/
练习
/
简单链表
简单链表

简单链表

简单

简介

你在一家音乐流媒体公司工作。

你的任务是为你的音乐播放器应用创建一个播放列表功能。

说明

为音乐播放器应用写一个原型。

在原型中,每首歌就简单地用一个数字表示。 给定一个数字范围(也就是歌曲 ID),创建一个单向链表。

给定一个单向链表,你应该能够反转这个链表,从而按相反的顺序播放歌曲。

Note

链表是计算机科学中的一种基础数据结构,常被用来实现其他数据结构。

最简单的链表是单向链表。 也就是说,每个元素(或称“节点”)都包含数据,以及指向链表中下一个节点的指针。

如果你想更深入地了解链表,可以看看这篇文章,它用直观的插图做了讲解。

这个练习在 Python 中的结构

虽然stacks和queues可以用lists、collections.deque、queue.LifoQueue和multiprocessing.Queue来实现,但这个练习要求的是用_自制_的单链表实现的“后进先出”(LIFO)栈:


表示用链表实现的栈的示意图。最左侧是一个带虚线边框的圆,名为 New_Node,还有两条指向右侧的点线箭头。New_Node 上写着“(becomes head) - New_Node - next = node_6”。上方那条点线箭头标着“push”,指向位于右上方的 Node_6。Node_6 上写着“(current) head - Node_6 - next = node_5”。下方那条点线箭头标着“pop”,指向一个方块,方块上写着“gets removed on pop()”。Node_6 有一条实线箭头向右指向 Node_5,Node_5 上写着“Node_5 - next = node_4”。Node_5 有一条实线箭头向右指向 Node_4,Node_4 上写着“Node_4 - next = node_3”。这个模式一直延续到 Node_1,Node_1 上写着“(current) tail - Node_1 - next = None”。Node_1 有一条点线箭头向右指向一个写着“None”的节点。


不要把它和用动态数组或列表实现的LIFO栈搞混,后者底层可能用的是list、queue或array。 基于动态数组的stacks,head位置不同,时间复杂度(Big-O)和内存占用也不同。


表示用数组/动态数组实现的栈的示意图。最右侧是一个带虚线边框的方块,名为 New_Node,还有两条指向左侧的点线箭头。New_Node 上写着“(becomes head) -  New_Node”。上方那条点线箭头标着“append”,指向位于左上方的 Node_6。Node_6 上写着“(current) head - Node_6”。下方那条点线箭头标着“pop”,指向一个带点线轮廓的方块,方块上写着“gets removed on pop()”。Node_6 有一条实线箭头向左指向 Node_5。Node_5 有一条实线箭头向左指向 Node_4。这个模式一直延续到 Node_1,Node_1 上写着“(current) tail - Node_1”。


关于一些需要考虑的地方,可以参考这两个 Stack Overflow 问题:基于数组 vs 基于列表的栈和队列 和 数组栈、链式栈和栈之间的区别。 想进一步了解链表、LIFO栈以及 Python 中其他抽象数据类型(ADT):


Python 中的类

在 Python 中,链表的“经典”实现通常需要一个或多个classes。 想好好入门classes,可以看classes和配套练习ellens-alien-game,或者官方 Python 教程的类部分。


Python 中的特殊方法

这个练习的测试会对你的LinkedList调用len()。 为了让len()正常工作,你需要创建一个__len__特殊方法。 关于在 Python 中实现特殊方法或“dunder”方法的细节,请参阅Python 文档:对象的基本定制和Python 文档:object.len(self)。


构建迭代器

想支持对LinkedList进行循环遍历或反转,你需要实现__iter__特殊方法。 关于实现细节,请参阅为类实现迭代器。


自定义异常和抛出异常

有时候,在你的代码里既需要自定义异常,也需要用raise来抛出异常。 这样做时,一定要附上有意义的错误信息,说明错误的来源是什么。 这能让你的代码更易读,对调试也有很大帮助。

自定义异常可以通过新的异常类来创建(详见classes),它们通常是Exception的子类。

如果你知道错误来源会是某种异常_类型_的派生,可以选择继承 Exception 类下的某个built in error types。 抛出错误时,仍然应该附上有意义的信息。

这个练习要求你创建一个_自定义异常_,当链表为空时将它抛出/“throw”。 只有自定义了合适的异常、用raise抛出这些异常,并附上合适的错误信息,测试才会通过。

要自定义一个通用的_异常_,可以创建一个继承自Exception的class。 抛出带信息的自定义异常时,把这条信息作为参数传给exception类型:

# subclassing Exception to create EmptyListException
class EmptyListException(Exception):
    """Exception raised when the linked list is empty.

    message: explanation of the error.

    """
    def __init__(self, message):
        self.message = message

# raising an EmptyListException
raise EmptyListException("The list is empty.")
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Python Exercism

准备好开始 简单链表 了吗?

注册 Exercism,借助 17 个概念146 个练习 和真人导师指导,学习并掌握 Python,全部免费。