本文主要借助对C++的标准模板库STL中实现的数据结构的学习和使用来加深对数据结构的理解,即联系数据结构的理论分析和具体的应用实现(STL),本文是系列总结的第一篇,主要针对线性表中的顺序表(动态数组)STL vector进行分析和总结。
由于前段时间对台大的机器学习基石和技法课程进行了学习,发现在具体的实现中常常涉及到各种类型的数据结构,比如线性表、二叉树、图等,在使用这些数据结构时感到有些吃力,主要是对一些基本的数据结构理解的不够,所以趁着暑假假期,最近一段时间总会抽空复习一下数据结构,参考的教材是王立柱编著的《C/C++与数据结构》,在具体的应用中采用的是C++标准模板库STL中对应实现的数据结构,主要参考的是MSDN文档。
正文
基本数据结构
数据结构中最简单的一个实现就是线性表中的顺序表,其数据结构如图所示:
数据结构上的实现为:
typedef struct
{
DataType *m_pData;
int m_nMax,m_nSize;
}SeqList;
typedef int DataType;
从结构上看,顺序表就是一个动态数组,需要有一个容量为m_nMax和一个用来记录表中已经有多少数据的m_nSize。而动态数组的容量在运行过程中是可以扩充和缩减的。因此也就比单纯的数组具有更加灵活的结构!
顺序表在C++的标准模板库中的实现
对应在C++的STL标准模板库中的实现是模板 vector:
<span style="font-size:18px;">template < class Type, class Allocator = allocator<Type> > class vector</span>
其中,Type是vector中存储的数据类型,使用时要包括头文件<vector>和命名空间std。
vector模板类中使用的数据类型
首先先认识一下vector模板类中最常使用的数据类型:
iterator 迭代器
用来提供对vector中元素随机访问的一个数据类型,可读可修改。
const_iterator 常量迭代器
用来提供对vector中元素随机访问,只能读一个const不变的元素。具体使用如下:
<span style="font-size:18px;">// vector_begin.cpp // compile with: /EHsc #include <vector> #include <iostream> int main() { using namespace std; vector<int> c1; vector<int>::iterator c1_Iter; vector<int>::const_iterator c1_cIter; c1.push_back(1); c1.push_back(2); cout << "The vector c1 contains elements:"; c1_Iter = c1.begin(); for (; c1_Iter != c1.end(); c1_Iter++) { cout << " " << *c1_Iter; } cout << endl; cout << "The vector c1 now contains elements:"; c1_Iter = c1.begin(); *c1_Iter = 20; for (; c1_Iter != c1.end(); c1_Iter++) { cout << " " << *c1_Iter; } cout << endl; // The following line would be an error because iterator is const // *c1_cIter = 200; }</span>
vector模板类的成员函数
assign
擦除改vector,并复制具体的元素到这个矢量中。<span style="font-size:18px;">// vector_assign.cpp // compile with: /EHsc #include <vector> #include <iostream> int main( ) { using namespace std; vector<int> v1, v2, v3; vector<int>::iterator iter; v1.push_back(10); v1.push_back(20); v1.push_back(30); v1.push_back(40); v1.push_back(50); cout << "v1 = " ; for (iter = v1.begin(); iter != v1.end(); iter++) cout << *iter << " "; cout << endl; v2.assign(v1.begin(), v1.end()); cout << "v2 = "; for (iter = v2.begin(); iter != v2.end(); iter++) cout << *iter << " "; cout << endl; v3.assign(7, 4) ; cout << "v3 = "; for (iter = v3.begin(); iter != v3.end(); iter++) cout << *iter << " "; cout << endl; }</span>输出为:
front和back
分别返回vector中第一个和最后一个元素的引用。使用案例如下:<span style="font-size:18px;">// vector_back.cpp // compile with: /EHsc #include <vector> #include <iostream> int main() { using namespace std; vector <int> v1; v1.push_back( 10 ); v1.push_back( 11 ); int& i = v1.back( ); const int& ii = v1.front( ); cout << "The last integer of v1 is " << i << endl; i--; cout << "The next-to-last integer of v1 is "<< ii << endl; }</span>输出为:swap在两个vector之间交换元素;size获取表中数据的个数;resize重新调整vector的数据个数;reserve保留一个最小长度的存储空间;push_back(size加1)和pop_back(size-1)实现的是在vector尾部插入和删除数据;insert在特定的位点插入一个或许多元素;erase擦掉一个或者一段范围内的元素。clear是擦除整个vector中的元素(size=0)。
重载运算符
[]使得vector可以像数组一样访问;=使得vector可以直接用右边的矢量去替换,而不用对左边进行存储空间的初始化等操作。其他注意事项和使用经验
有了以上的说明,我们在使用时,还需要知道一下的几种初始化,vector嵌套的定义和使用小技巧等:
vector<vector<float>> test = vector<vector<float>>(5,vector<float>(6, 0.0f));
这样就初始化了一个二维的float 型 vector,现在test里面装有5个float型的vector<float>,而这个float型的vector<float>又被初始化为6个0.0f的一维vector。
在C++中常常使用引用&作为函数的形参,比如vectorMutiply(vector<float>& x1, vector<float>& x2);
定义完vector,没有进行初始化操作,直接采用push_back()的方法可以进行插入,vector的最大存储空间capacity会动态的增加,用户不需要进行手动的调整。另外,可以直接利用等号=进行copy赋值操作,它的空间会系统自动分配。