数据结构之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方法等。
版权声明:本文为博主原创文章,未经博主允许不得转载。