数据结构之Vector

最近看了邓俊辉版的《数据结构》,收获颇多,特将数据结构的实现以及一些算法在此进行整理,以强化记忆。

首先记录一下第一章Vector的实现以及选择排序冒泡排序合并排序等算法。


简单说一下看懂此源码的要求:

1. 简单掌握c++模板类

2. 指针的实质掌握比较好

3. const的具体用法

4. 默认参数的使用

5. 简单的移位运算

6. 掌握c++中引用的实质

7. 运算符重载

8. this指针的实质


从大二年级开始学习数据结构,以为有了点基础之后看这本书会简单些,谁知道里面涉及到c++的很多知识,在经过好几次的回炉仔细学习c++之后,才掌握了此书的一些皮毛。不得不说,这是一本难得一见的好书,其中的很多编程技巧很是受用,下面详细介绍Vector类。


1> 首先介绍Vector类的构造函数,这里用到了构造函数重载,代码如下。

	Vector(int c = DEFAULT_CAPACITY, int s = 0, T v = 0)//用到了默认参数
	{
		_elem = new T[_capacity = c];					//创建T数据类型的数组
		for (_size = 0; _size < s; _elem[_size++] = v);	//此处的编程技巧应有掌声啊,赋初值
	}

	Vector(T const* A, Rank n)
	{
		copyFrom(A, 0, n);		//后面详述此方法
	}

2> 下面介绍方法copyFrom。此方法是用一个数组初始化Vector,相当于将数组中的指定区间依次放入Vector中。具体代码如下所示。

template<typename T>
void Vector<T>::copyFrom(T const* A, Rank lo, Rank hi)
{
	_elem = new T[ _capacity = 2 * (hi - lo)];//创建一个2倍于区间的数组
	_size = 0;
	while (lo < hi)
	{
		_elem[_size++] = A[lo++];			//依次放入
	}
}

3> 插入作为Vector的基本方法当然是不能缺的,这里的插入方法的两个参数分别为指定下标秩,和值。具体代码如下所示。

template<typename T>
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;
}
注:这里有两点需要注意,一是需要考虑扩容,当向量内值的总数达到某个阈值时,需要考虑扩容。二是注意往后挪元素时,需要从后面开始,如果从前面开始挪的话,会因覆盖而失败。


4> 当向量内的值的总数达到某个阈值时,需要考虑扩容,这和STL里面实现的vector性质一样,都是扩充于当前容量的2倍,然后将元素复制过去,最后记得删除原来数组的内存空间。具体代码如下所示。

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];		//创建2倍于当前容量的数组
	for (int i = 0; i < _size; ++i)
	{
		_elem[i] = oldElem[i];			//将元素复制进去
	}

	delete[] oldElem;					//释放内存空间
}

5> 有了插入,必然少不了删除功能。此处的删除,需要指定秩,即下标。具体代码如下所示。
template<typename T>
T Vector<T>::remove(Rank r)
{
	T e = _elem[r];
	remove(r, r + 1);	//删除制定区间的元素,此处即r

	return e;
}

6> 下面介绍步骤5未介绍的remove方法。此方法的功能为删除指定区间的元素。具体代码如下所示。

template<typename T>
int Vector<T>::remove(Rank lo, Rank hi)
{
	if (lo == hi)
		return 0;

	while (hi < _size)
		_elem[lo++] = _elem[hi++];	//将删除区间后面的元素直接复制进删除区间
	_size = lo;						//更新规模,如果删除区间大于删除区间后方区间,
									//则直接丢弃尾部
	shrink();						//缩容

	return hi - lo;
}
注:此处提供了缩容方法,下面将详细介绍。

7> 提供此方法的原因待定,具体代码如下所示。

template<typename T>
void Vector<T>::shrink()
{
	if (_capacity < DEFAULT_CAPACITY << 1)	//此种情况不收缩
		return;
	if (_size << 2>_capacity)				//以25%为界
		return;
	T* oldElem = _elem;						//记录向量
	_elem = new T[_capacity >>= 1];			//a>>=1相当于 a=a>>1 即除以2
	for (int i = 0; i < _size; i++)
	{
		_elem[i] = oldElem[i];				//复制进数组中
	}

	delete[] oldElem;						//删除所占内存空间
}
8> 插入和删除功能方法实现了,下面介绍查找方法。具体代码如下所示。

template<typename T>
Rank Vector<T>::find(T const& A, Rank lo, Rank hi) const
{
	while ((lo < hi--) && A != _elem[hi]);  //倒序查找
	return hi;								//失败时,返回lo-1
}
注:个人感觉这里缺少断言判断lo和hi的值是否在合理范围内,后期再进行完善。

9> 向量重载了[]下标操作,下面介绍此下标的重载。具体代码如下所示。

template<typename T>
T& Vector<T>::operator[](Rank i) const
{
	return _elem[i];
}
10>此向量不仅重载了[]操作符,还重载了=运算符,实现向量的赋值操作。具体代码如下所示。

template<typename T>
Vector<T>& Vector<T>::operator=(Vector<T> const& V)
{
	if (_elem)				//如果此对象不为空
		delete []_elem;		//删除
	copyFrom(V._elem, 0, V._size);	//复制V对象中的_elem数组中的所有元素
	return *this;			//返回vector对象的引用
}

11> 判断向量中存储元素的大小。具体代码如下所示。

Rank size()
	{
		return _size;
	}
12>判断向量是否为空,具体代码如下所示。

<span style="font-size:18px;">bool empty()
	{
		return !_size;
</span><span style="font-size:12px;">	}</span>
注:c++中bool类型只能存储两个值,0或者1,并且一般情况下只占一个字节。任何非0的值取反都等于0。


Vector向量的基本功能实现大致如此,后续再补上其中的排序算法以及一些杂项功能。


感悟:

1)能动手时别逼逼。在更改指针指向的数组中的元素时,本以为将指针传向别的方法,不会动到数组本身,但是在经过动手实践以及思考以前的知识时发现,只要拿到了指针指向的内存空间,并且此内存空间不是const修饰的,那么可以在任何方法中更改。

   2) 在c++编程的过程中,应该多站在编译器的角度考虑问题。

   3)指针,还是指针。对指针的追求永无止境。

   4) 掌握一些编程技巧。如恰当的使用移位以及灵活运用for方法等。









版权声明:本文为博主原创文章,未经博主允许不得转载。

posted @ 2015-10-22 00:36  会孵蛋的鱼  阅读(689)  评论(0编辑  收藏  举报