最近看了邓俊辉版的《数据结构》,收获颇多,特将数据结构的实现以及一些算法在此进行整理,以强化记忆。
首先记录一下第一章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方法等。