1
0
mirror of https://github.com/apachecn/lmpythw-zh.git synced 2025-05-31 05:27:43 +00:00
lmpythw-zh/ex15.md
wizardforcel 44a7732aa4 ex15
2017-08-07 11:58:47 +08:00

78 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 练习 15栈和队列
> 原文:[Exercise 15: Stacks and Queues](https://learncodethehardway.org/more-python-book/ex15.html)
> 译者:[飞龙](https://github.com/wizardforcel)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
当处理数据结构时,你将经常遇到类似于另一种结构的结构。`Stack`类似于练习13中的`SingleLinkedList`,以及`Queue`类似于练习14中的`DoubleLinkedList`,唯一区别是`Stack``Queue`限制了可能的操作,以简化它们的使用方式。这有助于减少缺陷,因为你不能意外地像`Queue`那样使用`Stack`并导致问题。在`Stack`中,节点被“压入”“栈顶”,然后从顶部“弹出”。在队列中,节点压入“尾部”,之后从“头部”弹出。这些操作都是`SingleLinkedList``DoubleLinkedList`的简化,其中`Stack`只允许`push``pop`操作,`Queue`只允许`shift``unshift`
> 译者注:实际上是`push`和`unshift`。
当可视化堆栈时你应该想到你的地板上的一堆书。想像我在书架上的那种很重的艺术书如果我堆叠了20个可能会重约100磅。当你为这些书构建栈的时候你不能抬起整个栈并且把书放在底部对吧你把书放在栈的顶部。你把它放在那儿但我们也可以使用“推”描述这个动作。如果你想从栈中获取一本书你可能会抬起一些书然后抓住一本书但是最终你可能要从顶部拿出一些书才能获取底部得数。你可以从顶部抬起每本书或者在我们的例子中我们会说“从顶部弹出一本书”。
如果你想像在银行排队,队列有“头部”和“尾部”,可视化队列是最简单的。通常有一个绳索迷宫,它的末尾有一个入口,出口处是检票员。你可以通过进入这条绳索迷宫的“尾部”进入队列,我们​​称之为`shift`,因为这是`Queue`数据结构中的常见编程属于。一旦你进入银行(队列),你不能越过等候线然后离开,否则其余的人会生气。所以你必须等待,随着你前面的每个人都离开了等候线(对你而言是`unshift `),你离“头部”更近了。一旦你达到了头部,那么你可以退出,我们称之为`unshift `
很多时候,你可以找到数据结构的真实世界示例,来帮助你可视化其工作原理。你现在应该花点时间来绘制这些场景,或者实际上得到书籍的栈并测试这些操作。你可以找到与`Stack``Queue`类似的其他真实情况吗?
## 挑战练习
我现在打算让你做一个基于代码的挑战练习,并且从它们的描述中实现数据结构。在这个挑战中,你首先需要使用这里的起始代码,以及你从练习 13 中了解的`SingleLinkedList`,实现`Stack`数据结构。完成之后,你将尝试从零开始实现`Queue`数据结构。
`StackNode`节点类几乎和`SingleLinkedListNode`相同,而事实上我只是复制过来并更名:
```py
class StackNode(object):
def __init__(self, value, nxt):
self.value = value
self.next = nxt
def __repr__(self):
nval = self.next and self.next.value or None
return f"[{self.value}:{repr(nval)}]"
```
`Stack `控制类和`SingleLinkedList `十分类似,除了我使用`top `代替了`first`。这样匹配`Stack`的概念。
```py
class Stack(object):
def __init__(self):
self.top = None
def push(self, obj):
"""Pushes a new value to the top of the stack."""
def pop(self):
"""Pops the value that is currently on the top of the stack."""
def top(self):
"""Returns a *reference* to the first item, does not remove."""
def count(self):
"""Counts the number of elements in the stack."""
def dump(self, mark="----"):
"""Debugging function that dumps the contents of the stack."""
```
现在你的挑战是实现`Stack`,并为其执行测试,类似于在练习 13 中进行的测试。请确保你的测试涵盖了每一个操作,你可以以任何方式。记住,尽管如此,堆栈的`push`操作必须在顶部,所以有到顶部的链接。
一旦你使`Stack`正常工作,你应该实现`Queue`,但它基于`DoubleLinkedList`。(译者注:其实单链表也行,因为只有尾部弹出的操作比较困难。你可以在尾部插入,在头部弹出。)`Stack`中的内容应该与`SingleLinkedList`基本内部结构相同,只需更改允许的功能。`Queue`也一样。花点时间来绘制队列的工作原理,然后弄清楚它如何限制`DoubleLinkedList`。一旦你完成了,创建你的队列。
## 破坏它
破坏这些数据结构仅仅是不要维持约束。看看如果一个操作无法使用正确的尾部会发生什么。
你可能还注意到,它有“偏移一位”的持久性错误。在我的设计中,当结构为空时,我设置了`self.top = None`。这意味着当你达到 0 个元素时,你必须对`self.top`做一些特殊处理。一个替代方法是使`self.top`总是指向一个`StackNode`(伪造的头节点),并假设当你有这个最后的元素时,结构是空的。尝试它,看看它如何改变你的实现。这样会更容易出错还是更不容易出错?
## 深入学习
这些数据结构有很多操作是非常低效的。回顾你为每个数据结构编写的代码,并尝试猜测哪些函数最慢。一旦你有了想法,尝试解释为什么他们可能很慢。研究其他人对这些数据结构的看法。在练习 18 和 19 中,你将学习对这些数据结构进行一些性能分析并进行调整。
最后,你真的需要实现一个全新的数据结构吗,还是简单地“包装” `SingleLinkedList``DoubleLinkedList`数据结构?这如何改变你的设计?