目标
理解栈、队列、双端队列、列表等抽象数据类型
能够用python列表实现栈、队列、和双端队列。
理解前中后序表达式
使用栈来计算后序表达式
使用栈将中序表达式换成后序表达式
使用队列进行基本的时序模拟
理解栈、队列以及双端队列适用于解决何种问题。
能够使用“节点与引用”模式将列表实现为链表。
能够从性能方面比较自己的链表实现与Python 的列表实现。
笔记正文
前后的相对位置保持不变,被称作线性数据结构。
可以看作有两端(左或右 前或后 顶或底)
用于区分的是如何添加和移除
栈
又叫做“下堆栈”,是一个有序集合。操作和移除在同一边-顶端,另一边是低端。
新添加的被先移除叫做-LIFO(last-in-first-out)
比如此时,python将会被先取出。
比如网页浏览调回按钮
网页1
网页2
网页3
最新打开网页1,然后返回到网页2,最后到网页3
3.3.2 栈抽象数据类型
Stack()创建一个空栈。它不需要参数,且会返回一个空栈。
push(item)将一个元素添加到栈的顶端。它需要一个参数item,且无返回值。
pop()将栈顶端的元素移除。它不需要参数,但会返回顶端的元素,并且修改栈的内容。
peek()返回栈顶端的元素,但是并不移除该元素。它不需要参数,也不会修改栈的内容。
isEmpty()检查栈是否为空。它不需要参数,且会返回一个布尔值。
size()返回栈中元素的数目。它不需要参数,且会返回一个整数。
3.3.3 用Python 实现栈
明确定义栈抽象数据类型之后,我们开始用Python 来将其实现。如前文所述,抽象数据类
型的实现常被称为数据结构。
要实现这样的抽象数据类型,那么就可以创建新的类
使用原生集合列表就能够实现,只要确认一个顶端,就能够一直执行。一旦确定了顶端,所有的操作就可以利用append 和pop 等列表方法来实现。
下面这个代码假设列表的尾部是栈的顶端。栈增长时(进行push操作时候,新的元素添加到最后,pop操作也会改变这一端)
1 class Stack:
2 def __init__(self):
3 self.items = []
4
5 def isEmpty(self):
6 return self.items == []
7
8 def push(self, item):
9 self.items.append(item)
10
11 def pop(self):
12 return self.items.pop()
13 def peek(self):
15 return self.items[len(self.items)-1]
16
17 def size(self):
18 return len(self.items)
值得注意的是,也可以选择将列表的头部作为栈的顶端。不过在这种情况下,便无法直接使
用pop 方法和append 方法,而必须要用pop 方法和insert 方法显式地访问下标为0 的元素,
即列表中的第1 个元素。代码清单3-2 展示了这种实现。
1 class Stack:
2 def __init__(self):
3 self.items = []
4
5 def isEmpty(self):
6 return self.items == []
7
8 def push(self, item):
9 self.items.insert(0, item)
10
11 def pop(self):
12 return self.items.pop(0)
13
14 def peek(self):
15 return self.items[0]
16
17 def size(self):
18 return len(self.items)
改变抽象数据类型的实现却保留其逻辑特征,这种能力体现了抽象思想。不过,尽管上述两
种实现都可行,但是二者在性能方面肯定有差异。append 方法和pop()方法的时间复杂度都是
O(1),这意味着不论栈中有多少个元素,第一种实现中的 push 操作和 pop 操作都会在恒定的时
间内完成。第二种实现的性能则受制于栈中的元素个数,这是因为insert(0)和pop(0)的时间
复杂度都是O(n),元素越多就越慢。显而易见,尽管两种实现在逻辑上是相等的,但是它们在
进行基准测试时耗费的时间会有很大的差异。
时间不同,一个的时间复杂度是O(n)一个是O(1)
3.3.6 将十进制数转换成二进制数
十进制233 和二进制数11101001 可以按这个形式表示
3.3.7 前序、中序和后序表达式