向量——vector (C++)

最为基本的线性结构统称为序列,根据其中数据项的逻辑次序与其物理存储地址的对应关系不同,又可进一步地将序列区分为向量(vector)和列表(list)。

在向量中,所有数据项的物理存放位置与其逻辑次序完全吻合,此时的逻辑次序也称为秩(rank)

在列表中,逻辑上相邻的数据项在物理上未必相邻,而是采用间接定址的方式通过封装后的位置(position)相互引用。

 

Vector模板类

/*************************************************************************
	> File Name: vector.h
	> Author: amoscykl
	> Mail: [email protected] 
	> Created Time: 2018年07月02日 星期一 20时51分45秒
 ************************************************************************/

typedef int Rank;	//秩
#define DEFAULT_CAPACITY  3			//默认的初始容量

template <typename T> class  Vector {	//向量模板类
protected:
	Rank _size; int _capacity; T* _elem;		//规模,容量,数据区
	void copyFrom(T const* A, Rank lo, Rank hi);		//复制数据区间A[lo,hi]
	void expand();						//空间不足时扩容
	void shrink();						//装填因子过小时压缩
	bool bubble (Rank lo, Rank hi);		//扫描交换
	void bubbleSort( Rank lo, Rank hi);	//起泡排序算法
	Rank max(Rank lo, Rank hi);			//选取最大元素
	void selectionSort(Rank lo, Rank hi);	//选取最大元素
	void merge(Rank lo, Rank mi, Rank hi);	//归并算法
	void mergeSort(Rank lo, Rank hi);		//归并排序算法
	Rank partition(Rank lo, Rank hi);		//轴点构造算法
	void quickSort(Rank lo, Rank hi);		//快速排序算法
	void heapSort(Rank lo, Rank hi);		//堆排序

public:
// 构造函数
	Vector( int c = DEFAULT_CAPACITY, int s = 0, T v = 0)		//容量为c、规模为s、所有元素初始化为v
	{	_elem = new T[_capacity = c]; for( _size = 0; _size < s; _elem[_size++] = v);	}	// s <= c
					/*动态数组*/
	Vector( T const* A, Rank n) { copyFrom(A,0,n); }				//数组整体复制
	Vector( T const* A, Rank lo, Rank hi) { copyFrom(A,lo,hi); }	//区间
	Vector( Vector<T> const& V) { copyFrom( V._elem, 0, V._size); }	//向量整体复制
	Vector( Vector<T> const& V, Rank lo, Rank hi) { copyFrom( V._elem, lo, hi); }	//区间
//析构函数
	~Vector() { delete [] __elem};
//只读访问接口
	Rank size() const { return _size; }		//规模	
	bool empty() const { return !_size; }	//判空
	int disordered() const;					//判断向量是否已排序
	Rank find ( T const& e ) const { return find (e,0,_size); }		//无序向量整体查找
	Rank find ( T const& e, Rank lo, Rank hi) const;				//无序向量区间查找
	Rank search ( T const& e) const						//有序向量整体查找
	{	return ( 0 >= _size ) ? -1 : search(e,0,_size); }
	Rank search (T const& e, Rank lo, Rank hi) const;	//有序向量区间查找
//可写访问接口
	T& operator[] (Rank r) const;			//重载下标操作符,可以类似于数组形式引用各元素
	Vector<T> & operator = (Vector<T> const& );	//重载赋值操作符,以遍直接克隆向量
	T remove ( Rank r );					//删除秩为r的元素
	int remove(Rank lo, Rank hi);			//删除区间之内的元素
	Rank insert(Rank r, T const& e);		//插入元素
	Rank insert(T const& e ) { return insert (_size,e); }	//默认作为末元素插入
	void sort(Rank lo, Rank hi);			//对区间排序
	void sort() { sort(0,_size); }			//整体排序
	void unsort (Rank lo, Rank hi );		//对区间置乱
	void unsort { unsort(0, _size); }		//整体置乱
	int deduplicate();						//无序去重
	int uniquify();							//有序去重
//遍历
	void traverse( void (* ) (T &) );				//遍历:使用函数指针 
	template <typename VST> void traverse (	VST& );	//遍历:使用函数对象
};	//Vector 


基于复制的构造方法: 

template <typename T>		//元素类型
void Vector<T>::copyFrom ( T const* A, Rank lo, Rank hi) {	//以数组区间A[lo,hi]为蓝本复制向量
	_elem = new T[_capacity = 2 * (hi - lo) ]; _size = 0;	//分配空间,规模清零
	while ( lo < hi) 
		_elem[_size++] = A[lo++];			//复制至_elem[0,hi-lo]
}

 重载向量复赋值操作符:

template <typename T> Vector<T>& Vector<T>::operator= (Vector<T> const& V) { //重载
	if (_elem )	  delete [] _elem;			//释放原有内容
	copyFrom(V._elem, 0, V.size() );		//整体复制
	return *this;							//返回当前对象的引用,以便链式赋值
}

 

 动态空间管理

1.静态空间管理

内部数组所占物理空间的容量,若在向量的生命期内不允许调整,则称作静态空间管理策略。该策略的空间效率难以保证。一方面,容量固定,可能会导致溢出;另一方面,即使预留出部分空间,但是很难明确界定一个合理的预留量。

上述copyFrom()方法,即使将容量取作初始规模的两倍,但仍有溢出的风险。

 

向量实际规模与其内部数组容量的比值(_size/_capacity)称作装填因子,它是衡量空间利用率的重要指标。

 

因此需要改用动态空间管理策略。其中有效的方法就是使用所谓的可扩充容量

 

2.动态空间管理

扩容算法expand()

 template <typename T> void Vector<T>::expand() {	//向量空间不足时扩容
	if (_size < _capacity ) return;			//尚未满员时,不必扩容
	if (_capacity < DEFAULT_CAPACITY ) _capacity = DEFAULT_CAPACITY;	//不低于最低容量
	T* oldElem = _elem;	  _elem = new T[_capacity <<= 1];		//左移一位.容量加倍
	for (int i = 0; i < _size; i ++)
		_elem[i] = oldElem[i];				//复制原向量内容(T为基本类型,或已重载赋值操作符=)
	delete [] oldElem;						//释放原有空间
}

 

缩容算法shrink()

向量的实际规模可能远小于内部数组的容量,当装填因子低于某个阀值,称数组发生了下溢,发生下溢时也有比要适当缩减内部数组容量,提高空间利用率。

template <typename T> void Vector<T>::shrink() {	//装填因子过小时压缩向量所占空间
	if (_capacity < DEFAULT_CAPACITY << 1) return;	//不致收缩到DEFAULT_CAPACITY一下 
	if (_size << 2 > _capacity) return ;			//以25%为界
	T* oldElem = _elem;	  _elem = new T[_capacity >>= 1];		//右移一位,容量减半
	for (int i = 0; i < _size; i++) _elem[i] = oldElem[i];		//复制原向量内容
	delete [] oldElem;								//释放原空间
}

 

 

重载向量操作符[]

template <typename T> T& Vector<T>::operator[] (Rank r) const	//重载下标操作符
{ return _elem[r]; }

 

无序向量元素查找接口find()

template <typename T>				//无序向量的顺序查找:返回最后一个元素e的位置;失败时,返回lo-1
Rank Vector<T>::find(T const& e, Rank lo, Rank hi) const {
	while( (lo < hi -- ) && (e != _elem[hi] ) );		//从后向前,顺序查找 
	return hi;						//若hi < lo, 则意味着失败;否则hi即命中元素的秩
}

 

 向量元素插入接口insert()

template <typename T>				//将e作为秩为r元素插入
Rank Vector<T>::insert (Rank r, T const& e) {	
	expand();						//若有必要,扩容。
	for (int i = _size; i > r ; i -- ) _elem[i] = _elem[i-1];	//自后向前,后继元素顺次后移一个单元
	_elem[r] = e;	_size++;		//置入新元素并更新容量
	return r;						//返回秩
} 


 区间删除:remove(lo,hi) 

template <typename T> int Vector<T>::remove ( Rank lo, Rank hi ) { //删除区间[lo,hi] 
	if (lo == hi) return 0;			//出于效率考虑,单独处理退化情况,比如remove(0,0)
	while (hi < _size) _elem[lo++] = _elem[hi++];		//[hi,_size]顺次前移hi-lo个单元
	_size = lo;						//更新规模,直接丢弃尾部[lo,_size=hi]区间 
	shrink();						//若有必要,则缩容
	return hi - lo;					//返回被删除元素的数目
}

单元素删除remove(r)

template <typename T> int Vector<T>::remove (Rank r) {  //删除向量中秩为r的元素,0 <= e < size
	T e = _elem[r];					//备份被删除元素
	remove(r,r+1);					//调用区间删除算法,等效于对区间[r,r+1]的删除
	return e;						//返回被删除元素
} 

 

 有序性甄别

template <typename T> int Vector<T>::disordered() const {	//返回向量中逆序相邻元素对的总数
	int n = 0;						//计数器
	for (int i = 1; i < _size; i ++)	//逐个检查_size-1 对相邻元素
		if (_elem[i-1] > _elem[i] )  n++;		//逆序则比较
	return n;						//向量有序当且仅当n=0
}

 

唯一化

有序向量uniquify()接口的高效实现

template <typename T> int Vector<T>::uniquify()	{	//有序向量重复元素剔除算法
	Rank i = 0, j = 0;				//各对互异“相邻”元素的秩 
	while(++j < _size)				//逐一扫描,直至末元素
		if ( _elem[i] != _elem[j] ) //跳过雷同者
			_elem[++i] = _elem[j];	//发现不同元素时,向前移至紧邻于前者右侧
	_size = ++i; shrink();			//直接截除尾部多余元素
	return j - i;					//向量规模变化量,即被删除元素总数
} 

 

查找

统一接口search()

template <typename T>       //在有序向量[lo,hi]内,确定不大于e的最后一个节点的秩
Rank Vector<T>::search (T const& e, Rank lo, Rank hi) const {
    return ( rand() % 2) ?  //按各50%的概率随机使用二分查找或Fibonacci查找
        binSearch(_elem, e, lo, hi) : fibSearch (_elem, e, lo, hi);
}

 

二分查找(版本A)

//二分查找
template <typename T>  static Rank binSearch(T* A, T const& e, Rank lo, Rank hi) {
	while( lo < hi) {	//每步迭代可能要做两次比较判断,有三个分支
		Rank mi = ( lo + hi) >> 1;		//以中点为轴点
		if( e < A[mi] ) hi = mi;		//深入前半段[lo,hi)继续查找
		else if (A[mi] < e)	lo = mi + 1;	//深入后半段(mi,hi)继续查找
		else	return mi;				//在mi处命中
	}	//成功查找可以提前终止
	return -1;							//查找失败
}		//有多个命中元素时,不能保证返回秩最大者。 

 

Fibonacci查找

略......

 

 

排序器

统一入口

template <typename T> void Vector<T>::sort (Rank lo, Rank hi) {	//向量区间[lo,hi]排序 
	switch( rand() % 5) {	//随机选取排序算法,可根据具体问题的特点灵活选取或扩充
		case 1: bubbleSort ( lo, hi); break;		//气泡排序
		case 2: selectionSort (lo, hi); break;		//选择排序
		case 3: mergeSort (lo,hi);	break;			//归并排序
		case 4: heapSort (lo,hi);	break;			//堆排序
		default: quickSort (lo,hi); break;			//快速排序
	}
}

冒泡排序

 //起泡排序
template <typename T>		//向量的起泡排序
void Vector<T>::bubbleSort( Rank lo, Rank hi)
{ while ( !bubble(lo, hi--) ); }	//逐躺做扫描交换,直至全序 

//扫描交换
template <typename T> bool Vector<T>::bubble (Rank lo, Rank hi) {	//一趟扫描交换
	bool sorted = true;		//整体有序标志
	while (++lo < hi)		//自左向右,逐一检查各对相邻元素
		if ( _elem[lo-1] > _elem[lo] ) {	//若逆序,则意味着尚未整体有序,并需要通过交换使局部有序
			sorted = false;
			swap( _elem[lo-1],_elem[lo]);
		}
	return sorted;			//返回有序标志
}

 

 

归并排序

归并排序的主体结构属典型的分治策略,可递归地描述和实现如代码:

//归并排序
template <typename T>		//向量归并排序
void Vector<T>::mergeSort (Rank lo, Rank hi) {
	if (hi - lo < 2)	return ;	//单元素区间自然有序,否则...
	int mi = ( lo + hi ) / 2;		//以中点为界
	mergeSort ( lo, mi ); mergeSort ( mi, hi );		//分别排序
	merge(lo,mi,hi);				//归并
}

template <typename T>		//有序向量的归并
void Vector<T>::merge ( Rank lo, Rank mi, Rank hi )	{	//各自有序的子向量[lo,mi)和[mi,hi)
	T* A = _elem + lo;		//合并后的向量A[0,hi-lo) = _elem[lo,hi)
	int lb = mi - lo;	T* B = new T[lb];		//前子向量B[0,lb) = _elem[lo,mi)
	for ( Rank i = 0; i < lb; B[i] = A[i++] ) 
		;	//复制前子向量
	int lc = hi - mi;	T* C = _elem + mi;		//后子向量C[0,lc_ = _elem[mi,hi)
	for (Rank i = 0, j = 0, k = 0; ( j < lb ) || ( k < lc) ; ) { //B[j]和C[k]中的小者绪至A末尾}
		if ( ( j < lb ) && ( ! ( k < lc ) || (B[j] <= C[k] ))) A[i++] = B[j++];
		if ( ( k < lc ) && ( ! ( j < lb ) || (C[k] <  B[j] ))) A[i++] = C[k++];
	}
	delete [] B;		//释放临时空间B
}		//归并后得到完整的有序


猜你喜欢

转载自blog.csdn.net/amoscykl/article/details/80906144