第一章 算法分析
1. 何为算法分析
算法分析关心的是基于所使用的计算资源比较算法。我们说甲算法比乙算法好,依据是甲算法有更高的资源利用率或使用更少的资源。
计算资源是什么?其实无非是算法解决问题时要占用的空间(内存)或者算法解决问题是 的执行时间。
1.1 大O计法
试图摆脱程序或计算机的影响而描述算法的效率时,量化算法的操作或步骤很重要。如果将 每一步看成基本计算单位,那么可以将算法的执行时间描述成解决问题所需的步骤数。确定合适 的基本计算单位很复杂,也依赖于算法的实现。
对于累加算法,计算总和所用的赋值语句的数目就是一个很好的基本计算单位。在 sumOfN 函数中,赋值语句数是 1(theSum = 0)加上 n(theSum = theSum + i 的运行次数)。可以 将其定义成函数 T ,令 T (n) = 1 +n 。参数 n 常被称作问题规模,可以将函数解读为“当问题规模 为n时,解决问题所需的时间是T(n),即需要1+n步”。
在前面给出的累加函数中,用累加次数定义问题规模是合理的。这样一来,就可以说处理前 100000 个整数的问题规模比处理前 1000 个整数的大。鉴于此,前者花的时间要比后者长。接下 来的目标就是揭示算法的执行时间如何随问题规模而变化。
计算机科学家将分析向前推进了一步。精确的步骤数并没有 T (n) 函数中起决定性作用的部 分重要。也就是说,随着问题规模的增长, T (n) 函数的某一部分会比其余部分增长得更快。最 后比较的其实就是这一起决定性作用的部分。数量级函数描述的就是,当 n 增长时,T (n) 增长最 快的部分。数量级(order of magnitude)常被称作大 O 记法(O 指 order),记作 O( f (n)) 。它提 供了步骤数的一个有用的近似方法。 f (n) 函数为 T (n) 函数中起决定性作用的部分提供了简单的 表示。
1.2 Python数据结构的性能
本节的目标是针对Python的列表和字典介绍如果用大O记法描述操作的性能。
1.2.1 列表
1.2.2 字典
第二章 基本数据结构
3.1 学习目标
理解栈、队列、双端队列、列表等抽象数据类型。
能够使用 Python 列表实现栈、队列和双端队列。
理解基础线性数据结构的性能。
理解前序、中序和后序表达式。
使用栈来计算后序表达式。
使用栈将中序表达式转换成后序表达式。
使用队列进行基本的时序模拟。
理解栈、队列以及双端队列适用于解决何种问题。
能够使用“节点与引用”模式将列表实现为链表。
能够从性能方面比较自己的链表实现与 Python 的列表实现。
3.2 何谓先行数据结构
我们首先学习4中简单而强大的数据结构。栈、队列、双端队列和列表都是有序的数据集合,其元素的顺序取决于添加顺序或移除顺序。一旦某个元素被添加进来,它与前后元素的相对位置将保持不变这种数据集合经常被称为线性数据结构。
举个例子:
# 数组
arr = {1,2,3,4,5}
# 如果我们添加一个元素
arr.append(6)
# 这样arr会变成{1,2,3,4,5,6}
线性数据结构可以看作有两段,分别称“左端”和“右端”,或者“前端”和“后端”。叫什么不重要。
3.3 栈
3.3.1 何谓栈
栈有时也被称为“下推栈”。它是有序集合,添加操作和移除操作总发生在同一端,即“顶端”,另一段则称为“底端”。
栈中的元素离底端越近,代表其在栈中的时间越长,因此栈的底端2具有非常重要的意义。最新添加的元素将被最先移除。这种排序原则被称为LIFO(last-in first out),即后进先出。它提供了一种基于在集合中的时间来排序的方式。最近添加的元素靠近顶端,旧元素则靠近底端。
举个例子:
比如这堆书,唯一露出封面的书就是顶端的那本。为了拿到其他某本书,需要移除压在其上面的书。再比如说一堆盘子,基本上都是要从上面一个一个拿。
再举个例子:由原生的Python数据对象构成的栈
图3-3展示了Python数据对象栈的创建过程和拆除过程。考虑到栈的反转特性,我们可以想到再使用计算机时的一些例子。例如,每一个浏览器都有一个返回按钮。当我们从一个网页跳转到另一个网页时,这些网页——实际上是URL——都被存放在一个栈中。当前在浏览的网页位于栈的顶端,最早浏览的网页则位于底端。如果点击返回按钮,则开始反向浏览这些网页。
3.3.2 栈抽象数据类型
栈抽象数据类型由下面的结构和操作定义。如前面所述,栈是元素的有序集合,添加操作与移除操作都发生在其顶端。栈的操作顺序是LIFO,它支持一下操作。
- stack()创建一个空栈。它不需要参数,并且会返回一个空栈;
- push(item)将一个元素添加到栈的顶端。它需要一个参数item,且无返回值
- pop()将栈顶端的元素移除,返回顶端的元素,并且修改栈的内容
- peel()返回栈顶端的元素,但是并不移除该元素。它不需要参数,也不会修改栈的内容
- isEmpty()检查栈是否为空
- size()返回栈中元素的数目
3.3.3 用Python实现栈
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item)
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[len(self.items) - 1]
def size(self):
return len(self.items)
3.3.4 匹配括号
匹配括号是指每个左括号都有与之对应的一个右括号,并且括号对有正确的嵌套关系。
3.3.5 十进制转换为二进制数
3.3.7 前序、中序和后序表达式
3.4 队列
3.4.1 何谓队列
队列是有序集合,添加操作发生在“尾部”,移除操作则发生在“头部”。新元素从尾部进入队列,然后一直向前移动到头部,直到称为下一个被移除的元素。
最新添加的元素必须在队列的尾部等待,在队列中时间最长的元素则排在最前面。这种排序的原则被称为FIFO(first in first out),即先进先出,也叫先到先得。
举个例子:
排队,比如进电影院,一头进另一头出。
3.4.2 队列抽象数据类型
- Queue()创建一个空队列。它不需要参数,且会返回一个空队列。
- enqueue(item)在队列的尾部添加一个元素。它需要一个元素作为参数,不返回任何值。
- dequeue()从队列的头部移除一个元素。它不需要参数,且会返回一个元素,并修改队列内容。
3.5 双端队列
3.5.1 何谓双端队列
双端队列是与队列类似的有序集合。它有一前、一后两端,元素在其中保持自己的位置。与队列不同的是:两段队列对在哪一段添加和移除元素没有任何限制。
3.5.2 双端队列抽象数据类型
- Deque()创建一个空的双端队列。它不需要参数,且会返回一个空的双端队列。
- addFront(item)将一个元素添加到双端队列的前端。它接受一个元素作为参数,没有返回值
- addRear(item)将一个元素添加到双端队列的后端。它接受一个元素作为参数,没有返 回值。
- removeFront()从双端队列的前端移除一个元素。它不需要参数,且会返回一个元素, 并修改双端队列的内容。
- removeRear()从双端队列的后端移除一个元素。它不需要参数,且会返回一个元素, 并修改双端队列的内容。
3.6 列表
列表是元素的集合,其中每一个元素都有一个相对于其他元素的位置。更具体的来说,这种列表称为无序列表。
List()创建一个空列表。它不需要参数,且会返回一个空列表。
add(item)假设元素 item 之前不在列表中,并向其中添加 item。它接受一个元素作为参数,无返回值。
remove(item)假设元素 item 已经在列表中,并从其中移除 item。它接受一个元素作为参数,并且修改列表。
search(item)在列表中搜索元素 item。它接受一个元素作为参数,并且返回布尔值。
isEmpty()检查列表是否为空。它不需要参数,并且返回布尔值。
length()返回列表中元素的个数。它不需要参数,并且返回一个整数。
append(item)假设元素 item 之前不在列表中,并在列表的最后位置添加 item。它接受一个元素作为参数,无返回值。
index(item)假设元素 item 已经在列表中,并返回该元素在列表中的位置。它接受一个元素作为参数,并且返回该元素的下标。
insert(pos, item)假设元素 item 之前不在列表中,同时假设 pos 是合理的值,并在位置 pos 处添加元素 item。它接受两个参数,无返回值。
pop()假设列表不为空,并移除列表中的最后一个元素。它不需要参数,且会返回一个元素。
【更新ing。。。】