数据结构之顺序表
2015-06-29 18:08
218 查看
顺序表是最简单的一种数据结构,数组就是一个简单的顺序表,大家可以利用对数组的认识来理解顺序表。顺序表的最大特点就是存储在一片连续的地址空间。这一特点决定了顺序表的优点和不足:
优点:由于地址连续,所以可利用一个首地址作为整个顺序表的标识。同时,地址连续的顺序表可以实现随机存储,也就是说可以利用下标访问元素,这一点最大的好处是可以节省访问时间,提高程序运行效率。
不足:为保证地址空间的连续性,顺序表每删除一个元素都要移动后面所有元素,这会造成很多不必要的操作。
由于顺序表实现简单,所以平时用的还是比较多的。以后各种复杂数据结构都是基于顺序表改进得到的。
顺序表源代码:
实现过程中要注意的两点:
一是重载赋值操作符(=)时为防止自身赋值出问题所做的判断,针对不同的类的,这种判断不能一概而论,有的可以直接利用某个元素的相等来判断(如本例)。而有的类则需要判断类中很多个元素,甚至要考虑为此重载相等操作符。这里最常犯的错误是没有判断或者直接利用”==”判断了却没有重载”==”。前者会造成自我赋值时的错误,而后者主要是在手写代码时要注意。
二是对输出操作符(<<)的重载。这里问题比较多,要多说几句。
首先是输出操作符一般重载为友元函数,且其形式是较为固定的,这一点不明白的可以参考相关资料。
其次是关于模板类的友元关系声明,C++支持三种模板类的声明:
1. 普通函数的友元声明
即将友元关系授予一个特定的非模板函数,这个情况最简单,可以直接声明
2. 一般模板函数的友元声明
这种情况下,函数本身也是一个模板函数,函数所用的模板和类的模板“没有关系”,即两个模板是不同的
这种情况下要注意友元声明前的template<typename T2>是必须要有的
3. 特定模板的友元声明
这种情况最为复杂,其要求将友元关系授予某些特定的模板函数,其中最常用的是和类模板相同的函数
这里要注意fun后面的<T>是要写的(不写不会再编译时出错,但会在使用该函数时出问题),而且函数必须再类前做出声明
更为复杂的情况是模板函数有以类的模板为参数
这里要想声明类,然后用类声明又有函数,再在类里面声明友元关系。本例中重载输出操作符及是这种情况。
最后要说明的是这些情况仅限于友元声明是的不同,友元函数实现是和正常情况下一样的。
优点:由于地址连续,所以可利用一个首地址作为整个顺序表的标识。同时,地址连续的顺序表可以实现随机存储,也就是说可以利用下标访问元素,这一点最大的好处是可以节省访问时间,提高程序运行效率。
不足:为保证地址空间的连续性,顺序表每删除一个元素都要移动后面所有元素,这会造成很多不必要的操作。
由于顺序表实现简单,所以平时用的还是比较多的。以后各种复杂数据结构都是基于顺序表改进得到的。
顺序表源代码:
</pre><p align="left"><pre name="code" class="cpp">#ifndef _SEQLIST_H #define _SEQLIST_H #include <iostream> const int defaultSize=10; template<typename dataType> class seqList;//声明模板类 template<typename dataType> std::ostream& operator<<(std::ostream&,const seqList<dataType>&);//声明模板函数 template<typename dataType> class seqList { public: seqList(const int size=defaultSize):maxSize(size),length(0)//(默认)构造函数 { elements=new dataType[maxSize]; } seqList(const seqList& other)//复制构造函数 { this->maxSize=other.maxSize; this->length=other.length; this->elements=new dataType[maxSize]; for(int i=0;i<this->length;++i) this->elements[i]=other.elements[i]; } seqList& operator=(const seqList& other)//赋值操作符 { if(this->elements==other.elements) return *this;//自我赋值,返回自身 delete[] this->elements; this->maxSize=other.maxSize; this->length=other.length; this->elements=new dataType[this->maxSize]; for(int i=0;i<this->length;++i) this->elements[i]=other.elements[i]; } ~seqList(){delete[] elements;}//析构函数 bool insertElement(const dataType&);//向表尾插入元素 bool deleteElement(const int&);//删除指定位置元素 dataType getElement(const int&) const;//返回指定位置的元素 bool changeElemet(const int&,const dataType&);//修改指定位置元素 template<typename dataType> friend std::ostream& //重载输出操作符 operator<< <dataType>(std::ostream&,const seqList<dataType>&); private: dataType* elements;//存放表元素 int maxSize;//表的最大容量 int length;//表的有效长度 }; template<typename dataType> //向表尾插入元素 bool seqList<dataType>::insertElement(const dataType& elmt) { if(length>maxSize) { cerr<<"The seqList is full!"<<endl; return false; } this->elements[length++]=elmt; return true; } template<typename dataType> //删除指定位置元素 bool seqList<dataType>::deleteElement(const int& index) { if(index>=maxSize||index>=length||index<0) { cerr<<"The subscrip is illegal!"<<endl; return false; } for(int i=index;i<length;++i) { this->elements[i]=this->elements[i+1]; } --length; return true; } template<typename dataType> //返回指定位置的元素 dataType seqList<dataType>::getElement(const int& index) const { if(index>=maxSize||index>=length||index<0) { cerr<<"The subscrip is illegal!"<<endl; } return this->elements[index]; } template<typename dataType> //修改指定位置元素 bool seqList<dataType>::changeElemet(const int& index,const dataType& elmt) { if(index>=maxSize||index>=length||index<0) { cerr<<"The subscrip is illegal!"<<endl; return false; } this->elements[index]=elmt; } template<typename dataType> //重载输出操作符 std::ostream& operator<<(std::ostream& os,const seqList<dataType>& sqLst) { for(int i=0;i<sqLst.length;++i) os<<sqLst.elements[i]<<" "; os<<std::endl; return os; } #endif
实现过程中要注意的两点:
一是重载赋值操作符(=)时为防止自身赋值出问题所做的判断,针对不同的类的,这种判断不能一概而论,有的可以直接利用某个元素的相等来判断(如本例)。而有的类则需要判断类中很多个元素,甚至要考虑为此重载相等操作符。这里最常犯的错误是没有判断或者直接利用”==”判断了却没有重载”==”。前者会造成自我赋值时的错误,而后者主要是在手写代码时要注意。
二是对输出操作符(<<)的重载。这里问题比较多,要多说几句。
首先是输出操作符一般重载为友元函数,且其形式是较为固定的,这一点不明白的可以参考相关资料。
其次是关于模板类的友元关系声明,C++支持三种模板类的声明:
1. 普通函数的友元声明
即将友元关系授予一个特定的非模板函数,这个情况最简单,可以直接声明
template<typenameT> class A { friend void fun(int&);//fun函数是非模板函数 };
2. 一般模板函数的友元声明
这种情况下,函数本身也是一个模板函数,函数所用的模板和类的模板“没有关系”,即两个模板是不同的
template<typenameT1> class B { template<typename T2> friend void fun(T2&);//fun是T2模板函数这和类的模板T1不同 };
这种情况下要注意友元声明前的template<typename T2>是必须要有的
3. 特定模板的友元声明
这种情况最为复杂,其要求将友元关系授予某些特定的模板函数,其中最常用的是和类模板相同的函数
template<typename T> void fun(T&);//必须再前面声明 template<typename T> class C { friendvoid fun<T>(T&);//fun函数所用模板和类相同 };
这里要注意fun后面的<T>是要写的(不写不会再编译时出错,但会在使用该函数时出问题),而且函数必须再类前做出声明
更为复杂的情况是模板函数有以类的模板为参数
template<typename T> class C;//先声明类 template<typename T> voidfun(T&,C<T>&);//必须再前面声明 template<typename T> class C { friendvoid fun<T>(T&,C<T>);//fun函数所用模板和类相同 };
这里要想声明类,然后用类声明又有函数,再在类里面声明友元关系。本例中重载输出操作符及是这种情况。
最后要说明的是这些情况仅限于友元声明是的不同,友元函数实现是和正常情况下一样的。
相关文章推荐
- doubango sipstack 数据结构
- 开源hsm代码学习笔记---数据结构的定义
- 排序算法之归并排序 分类: C/C++ 数据结构与算法 2015-06-29 16:46 190人阅读 评论(0) 收藏
- 简单数据结构之二叉树(C++实现)
- 数据结构和算法经典文章汇总
- 数据结构:回溯法与树的遍历
- Java数据结构-栈(2)
- Python数据结构之序列
- 单链表的基本操作
- 20150628菜鸟对数据结构的 疑问
- 数据结构的基本内容
- mongodb数据结构学习1--增删改查
- 数据结构与算法 介绍(笔记)
- 图示化各种数据结构和算法是怎么实现的
- acm 常用数据结构与算法专题(未分类均放于此)
- Redis数据结构分析
- 数据结构--顺序循环队列和链式队列
- 数据结构——二叉树的遍历
- 【采摘】CSDN谚语
- 数据结构--二进制转化为十进制的实现