前言:数组与其泛化
我们都在学习编程语言的过程中接触过数组,它相当于是编程语言本身自带的一种数据结构,在很多编程语言中,数组通常这样来表示:{1,2,3,4,……},而在线性代数中,我们都知道这种形式被用来表示“向量”(Vector)。我们为何不直接取线性代数里面的概念,对数组这种数据结构做一个推广呢?
秩
在数组中,我们访问元素是通过下标来进行的,数组的下标代表元素在数组中的位置,而在向量中,我们要引入秩的概念。
这个概念很好理解:一个向量的前面有r个元素,那么该元素的秩就是r,之后我们可以通过重载“[ ]”运算符,像数组中的访问下标一样访问向量元素,比如,向量V中一个元素的秩为r,那这个元素就可以通过V[r]来访问了。
这种通过秩访问的方式被称为“循秩访问”。
有序与无序
我们将把向量分为:元素不一定按由小到大的顺序排列的无序向量,和元素按照由小到大的顺序排列的有序向量两种,当然,作为指挥官,在检阅时你肯定希望你的士兵按身高顺序排列好,因为方便管理。所以针对这两种不同的向量,我们有与之对应的不同的算法接口,因为处理这两种向量的同一类算法的时间复杂度可能不同。
动态空间管理
作为指挥官,你一定希望在有需要时,你指挥的士兵编队里可以增加或减少一定的人数,而可以省去那些不必要的申请更改编队人数的手续,同时,根据编队人数的减少,所需的物资也能随之一起改变就更好了。
对于向量,我们就要采用这样的管理方式,使得资源的利用效率达到最大,也不用像数组那样,使用前要先声明一个数组长度,所以我们采用的是动态的空间管理,为了实现这一个功能,我们会用到扩容和缩容两种方法。
接口
我们接下来每一篇的任务,就是要构建自己的抽象数据类型,每篇结束之后,我们会得到的不止有数据结构和算法的一些理论知识,我们会得到一个个由自己编写的数据类型模板,以及可以对这种数据结构进行操作的算法接口,你可以理解为你作为指挥官,对数据士兵下达一个个指令所用的文书和传令兵。
对于向量(Vector)这种数据结构,我们要实现以下接口:
操作接口 | 功能 |
---|---|
size() | 报告当前向量中元素的规模 |
get® | 获取秩为r的元素 |
put(r,e) | 用e替换秩为r的元素的数值 |
insert(r,e) | 把e作为秩为r的元素插入 |
remove® | 删除秩为r的元素,返回该元素中原来存放的对象 |
disordered() | 判断所有元素是否已经按非降序排列 |
sort() | 使向量中的元素降序排列 |
find(e) | 查找等于e且秩最大的元素 |
search(e) | 查找目标元素e,返回不大于e且秩最大的元素 |
deduplicate() | 删除重复元素 |
uniquify() | 剔除重复元素 |
traverse() | 遍历向量中的所有元素,并对每一个元素用某种函数方法处理 |
向量模板类的实现
代码
实现向量模板类的C++代码如下(我们模板类名为Vector1):
typedef int Rank; //秩
#define DEFAULT_CAPACITY 3
template <typename T> class Vector1
{
protected:
Rank _size; //规模
int _capacity; //容量
T* _elem; //数据区
void copyFrom(T const* A, Rank low, Rank high); //复制数组区间为low到high
void expand(); //空间不足时扩展
void shrink(); //装填因子过小时压缩
bool bubble(Rank low, Rank high); //扫描交换
void bubblesort(Rank low, Rank high); //冒泡法排序
Rank max(Rank low, Rank high); //选取最大元素
void selectionSort(Rank low, Rank high); //选择排序
void merge(Rank low, Rank high); //归并
void mergeSort(Rank low, Rank high); //归并排序
Rank partition(Rank low, Rank high); //轴点构造
void quickSort(Rank low, Rank high); //快速排序
void heapSort(Rank low, Rank high); //堆排序
public:
Vector1(int c = DEFAULT_CAPACITY, int s = 0, T v = 0) //容量为c,规模为s,所有元素初始为v
{
_elem = new T[_capacity = c];
for (_size = 0; < s; _elem[_size] = v);
}
Vector1(T const* A, Rank n)
{
copyFrom(A, 0, n);
}
Vector1(T const* A, Rank low, Rank high)
{
copyFrom(A, low