CHAPTER 3 Data Abstraction
3.1. 抽象数据类型的代数接口 Algebraic Interfaces for Abstract Data Types
数据结构化中最核心的想法之一就是数据抽象。
有一点编程基础的都知道队列(queue)的逻辑,对数据元素进行排队(enqueue),然后按照收到的顺序将它们出队(dequeue)。
这里用t(α)表示一个set,α可能存在空队列,enqueue和dequeue可以作用于空队列,但是空队列没有dequeue结果。如果是一个偏函数(partial function),即“不完整的函数”,那么对于那么没有映射的元素,函数式写成。
如果是普通的编程到底为止就够了,但是关于形式化正确性证明,我们需要更丰富我们的数据类型,增加一种叫“specification”的类型,简写成“spec”。spec类型中一种比较突出的是algebraic,它指的是写出一组用数据类型操作的等式法则。下面是关于队列queue的两个法则:
(1)用上一章中讲到的表达形式可以写成(2):
(2)用分段函数的形式,写出完成的表达式(3):
关于数据类型list,表示一个由α元素组成的list
有两种表达方式,都是FIFO的规则:
“头进尾出”:
和“尾进头出”:
在实践中,这两个版本实际上都需要二次的时间(quadratic time),假设连接所花费的时间与第一个参数的长度成线性。有一个著名的、更聪明的实现,它实现了平摊常数时间(运行整个操作序列的线性时间),但我们需要扩展我们的代数风格来适应它。
3.2. 带有自定义等价关系的代数接口 Algebraic Interfaces with Custom Equivalence Relations
这里引入了新的数学运算符,等价关系“”,符合reflexivity 自反性、 symmetry 对称性和 transitivity 传达性。
在(4)中,如果横线上方两个条件成立,那么横线下方的逻辑也成立。
问:下图等号两边相等吗?
答案是不相等。
左边 = ([2], []);
右边 = (dequeue(enqueue(([1], []),2))), 因为enqueue(empty, 1) = ([1], [])
= (dequeue([1]. [2])) 因为enqueue(([1], []),2) = ([1]. [2])
= (dequeue(([], [2]), [1])) 因为dequeue([1]. [2]) = (([], [2]), [1])
= ([], [2])
看到这里会发现,这种数据结构是非准则化的,同一套逻辑可能有不同的表达方式。下一节讲的另一种表达式,可以让每个queue都有标准的表达。
3.3. 表达函数Representation Functions
修正后的队列类型定义
修正后的公理
3.4. 固定抽象数据类型的参数类型Fixing Parameter Types for Abstract Data Types
有限集
这一节引入了另一个经典的抽象数据类型:有限集。其中B代表Booleans集,T代表“true”,代表“false"。
对于一个没有排序的list:
但是对于一个有规律的有限集,比如说自然数集,大多数集都包含连续的数字,那么执行的时候就有了对应的窍门:
这套执行方法比寻常的基于常规list的方法要快上许多,直接把四次方降低为线性时间复杂度。