@[TOC](目录)
1. 数组到向量
数据结构按照逻辑次序的复杂程度可以分为线性结构、半线性结构以及非线性结构。最为基本的线性结构统称为序列(sequence),序列又可以分为向量(vector)和列表(list)。
1.1. 数组
数组(array)是C++和Java里的一种内置的数据类型,从0开始编号。若数组A[]存放空间的起始地址为A,且每个元素占用s个单位的空间,那么A[i]对应的物理地址为 A + i × s A + i \times s A+i×s
1. 2. 向量
向量是线性数组的泛化和抽象,是具有线性次序的一组元素构成的集合,各元素的秩互异。
2. 接口
2.1. ADT 结构
3. 构造和析构
3.1. 基于复制的构造方法
1 template <typename T> //元素类型
2 void Vector<T>::copyFrom(T const* A, Rank lo, Rank hi) { //以数组匙间A[lo, hi)为蓝本复刢向量
3 _elem = new T[_capacity = 2 * (hi - lo)]; _size = 0; //分配空间,觃模清零
4 while (lo < hi) //A[lo, hi)内癿元素逐一
5 _elem[_size++] = A[lo++]; //复刢至_elem[0, hi - lo)
6 }
由于向量内部含有动态分配的空间,默认的运算符"="不足以支持向量之间的直接赋值。重载向量的赋值运算符。
1 template <typename T> Vector<T>& Vector<T>::operator=(Vector<T> const& V ) { //重载赋值操作符
2 if (_elem) delete [] _elem; //释放原有内容
3 copyFrom(V._elem, 0, V.size()); //整体复刢
4 return *this; //迒回弼前对象癿引用,以便链式赋值
5 }
4. 动态空间管理
4.1. 静态空间管理
内部数组所占物理空间的容量,若在向量的生命期内不允许调整,则称作静态空间管理策略。
向量实际规模与其内部数组容量的比值(即_size/_capacity),亦称作装填因子(load
factor),它是衡量空间利用率的重要指标。
4.2 可扩充向量
当容量溢出时,另行申请一个容量更大的数组。
4.3 缩容
当装填因子低于某一阈值时,我们称数组发生了下溢(underflow)。
1 template <typename T> void Vector<T>::shrink() { //装填因子过小时压缩向量所占空间
2 if (_capacity < DEFAULT_CAPACITY << 1) return; //丌致收缩刡DEFAULT_CAPACITY以下
3 if (_size << 2 > _capacity) return; //以25%为界
4 T* oldElem = _elem; _elem = new T[_capacity >>= 1]; //容量减半
5 for (int i = 0; i < _size; i++) _elem[i] = oldElem[i]; //复刢原向量内容
6 delete [] oldElem; //释放原空间
7 }
每次删除操作之后,一旦空间利用率已降至某一阈值以下,该算法随即申请一个容量减半的新数组,将原数组中的元素逐一搬迁至其中,最后将原数组所占空间交还操作系统.
5. 常规向量、有序向量
唯一化
1 template <typename T> int Vector<T>::uniquify() { //有序向量重复元素剔除算法(高效版)
2 Rank i = 0, j = 0; //各对互异“相邻”元素癿秩
3 while (++j < _size) //逐一扫描,直至末元素
4 if (_elem[i] != _elem[j]) //跳过雷同者
5 _elem[++i] = _elem[j]; //収现丌同元素时,向前秱至紧邻亍前者右侧
6 _size = ++i; shrink(); //直接截除尾部夗余元素
7 return j - i; //向量觃模发化量,即被初除元素总数
8 }
二分查找
1 // 二分查找算法(版本A):在有序向量癿匙间[lo, hi)内查找元素e,0 <= lo <= hi <= _size
2 template <typename T> static Rank binSearch(T* A, T const& e, Rank lo, Rank hi) {
3 while (lo < hi) { //殏步迭代可能要做两次比较刞断,有三个分支
4 Rank mi = (lo + hi) >> 1; //以中点为轴点
5 if (e < A[mi]) hi = mi; //深入前半殌[lo, mi)继续查找
6 else if (A[mi] < e) lo = mi + 1; //深入后半殌(mi, hi)继续查找
7 else return mi; //在mi处命中
8 } //成功查找可以提前终止
9 return -1; //查找失败
10 } //有多个命中元素时,丌能保证迒回秩最大者;查找失败时,简单地迒回-1,而丌能指示失败癿位置