C++语言实现顺序表
顺序表的定义及其特点
顺序表的定义是:把线性表中的所有表项按照其逻辑顺序依次存储到从计算机存储中指定存储位置开始的一块连续的存储空间中。 这样,线性表中第一个表项的存储位置就是被指定的存储位置,第i个表项(2≤\leq≤ i ≤\leq≤n)的存储位置紧接在第i一1个表项的存储位置的后面。假设顺序表中每个表项的数据类型为T,则每个表项所占用存储空间的大小(即字节数)大小相同,均为 sizeof(T),整个顺序表所占用存储空间的大小为n×\times× sizeof(T),其中n表示线性表的长度。
顺序表的特点是:
(1)在顺序表中,各个表项的逻辑顺序与其存放的物理顺序一致,即第i个表项存储于
第i个物理位置(1≤\leq≤ i ≤\leq≤n).
(2)对顺序表中所有表项,既可以进行顺序访问,也可以进行随机访问。 也就是说,既可以从表的第一个表项开始逐个访问表项,也可以按照表项的序号(亦称为下标)直接访问表项。
顺序表类定义及其主要操作
顺序表的类声明
class SeqList
{
private:
T *data;//存放数据的动态数组
int maxSize;//最大可容纳表项的项数
int last; //当前已存表项的最后位置(从0开始)
void reSize(int newSize); //改变数组空间大小
public:
SeqList(int sz); //构造函数
SeqList(SeqList<T>& L); //复制构造函数
~SeqList() {delete[ ] data;} //析构函数
int Size(){//求表最大容量
return maxSize;
}
int Length() {//计算表长度
return last+1;
}
int Search(T x);//搜索x在表中位置,函数返回表项序号
int Locate(int i);//定位第i个表项,函数返回表项序号
bool Insert(int i, T& x);//插入x在第i个表项之后
bool Remove(int i, T& x);//删除第i个表项之后,通过x返回表项的值
bool GetData(int i,T& x);//取第i个表项的值,以x引用返回
bool SetData(int i,T& x);//用x修改第i个表项的值
bool IsEmpty(){//判断表是否空否,是返回true,否返回false
return (last==-1) ? true : false;
}
bool IsFull(){//判读表是否满否,是返回true,否返回false
return (last = maxSize - 1) ? true : false;
}
void input();//输入数据
void output();//打印数据
};
构造函数和复制构造函数
template <class T>
inline SeqList<T>::SeqList(int sz)
{
if (sz > 0) {
maxSize = sz; last = -1;
data = new T[maxSize]; //创建表存储数组
if (data == NULL) //动态分配失败
{
cout<<"储存分配错误!"<<endl;
}
}
}
template <class T>
inline SeqList<T>::SeqList(SeqList<T>& L)
{
//复制构造函数,用参数表中给出的已有顺序表初始化新建的顺序表
maxSize = L.Size();
last = L.Length()-1;
T value;
data = new T[maxSize];//创建顺序表储存数组
if(data == NULL){
cout<<"动态分配错误!"<<endl;
}
for(int i =1;i<=last-1;i++){
L.GetData(i, value);
data[i-1] = value;
}
}
调整顺序表空间大小
template <class T>
inline void SeqList<T>::reSize(int newSize){
//私有函数,扩充顺序表的储存数组空间的大小,新数组的元素个数为newSize.
if(newSize <= 0){
cout<<"无效数组的大小!"<<endl;
}
if(newSize != maxSize){
T *newarray = new T[newSize];
if(newarray == NULL){
cout<<"储存分配错误!"<<endl;
}
int n = last+1;
T *srcptr = data;//源数组的首地址
T *desptr = newarray;//目的数组的首地址
while(n--){//复制
*desptr++ = *srcptr++;
}
delete []data;//删除老数组
data = newarray;
maxSize = newSize;//复制新数组
}
}
搜索和定位操作
template <class T>
inline int SeqList<T>::Search(T x)
{
//搜索函数:在表中顺序搜索与给定值x匹配的表项,找到则函数返回该表项是第几个元素
//否则返回0,表示搜索失败
for(int i=0;i<=last;i++){
if(data[i] == x)
return i+1;
}
return 0;
}
template <class T>
inline int SeqList<T>::Locate(int i)
{
//定位函数:返回第i(1<=i<=lat+1)个表项的位置,否则函数返回0,表示定位失败
if(i>=1 && i<last+1){
return i;
}
else return 0;
}
插入与删除操作
template <class T>
inline bool SeqList<T>::Insert(int i, T& x)//插入x在第i个表项之后
{
//将新元素x插入到第i(0<=i<=last+1)个表项之后。函数返回插入成功的信息,若插入成功返回true,否则返回false.
//i=0时虚拟的,实际上是插入到第1个元素的位置。
if(last == maxSize - 1){
return false;//表满,不能插入
}
if(i<0 || i > last+1)
{
return false;//参数i不合理,不能插入
}
for(int j=last;j>=i;j--)
{
data[j+1]=data[j];//一次后移,空出第i号位置
}
data[i] = x;//插入
last++;//最后位置加一
return true;
}
template <class T>
inline bool SeqList<T>::Remove(int i, T& x)//删除第i个表项之后,通过x返回表项的值
{
//从表中删除第i(0<=i<=last+1)个表项,通过引用型参数x返回删除的元素值。函数返回删除成功的信息,删除成功返回true,删除失败返回false
if(last == -1){
return false;//表空,不能删除
}
if(i<1 || i>last+1){
return false;//参数i不合理,不能删除
}
x=data[i];//储存删除的元素
for(int j=i;j<=last;j++)
{
data[j-1] = data[j];//依次前移,填补
}
last--;//最后位置减一
return true;
}
顺序表的性能分析
顺序表所有操作的实现中,最复杂、最耗时的就是搜索、插入和删除运算的实现代码。分析顺序表的性能,主要是分析这3个操作的实现代码的时间复杂性。
int search(T& x)是顺序表的顺序搜索算法。 其主要思想是:
从表的开始位置起,根据给定值x,逐个与表中各表项的值进行比较。 若给定值与某个表项的值相等,则算法报告搜索成功的信息并返回该表项的位置;若查遍表中所有的表项,没有任何一个表项满足要求,则算法报告搜索不成功的信息并返回0(必要的话,可改一下算法,返回新表项应插人的位置)。
搜索算法的时间代价用数据比较次数来衡量。 在搜索成功的情形下,顺序搜索的数据比较次数可做如下分析。 若要找的正好是表中第1个表项,数据比较次数为1,这是最好情况;若要找的是表中最后的第n个表项,数据比较次数为n(设表的长度为n),这是最坏的情况 。若要计算平均数据比较次数,需要考虑各个表项的搜索概率p及找到该表项时的数据比较次数pip_ipi.搜索的平均数据比较次数ACN(average comparing number)为
ACN=∑i=1npi×ci\sum_{i=1}^n {p_i}\times c_i∑i=1npi×ci
计算平均数据比较次数是为了了解对表操作的整体性能。若考虑相等概率的情形。搜索各表项的可能性相同,有p1p_1p1=p2p_2p2=…=pnp_npn=1n\frac1 nn1,且搜索第1个表项的数据比较次数为1,搜计算平均值是为了解算法对表操作的整体性能。 若仅考虑相等概率的情形。搜索各表索第2个表项的数据比较次数为2,搜索第i号表项的数据比较次数为i,则
ACN=1n∑i=1ni=1n(1+2+3+...+n)==1+n2\frac1 n\sum_{i=1}^ni=\frac1 n(1+2+3+...+n)==\frac{1+n} 2n1∑i=1ni=n1(1+2+3+...+n)==21+n
即平均要比较n+12\frac{n+1} 22n+1个表项。
分析顺序表的插入和删除的时间代价主要看循环内的数据移动次数
在将新表项插入到第i个表项(0≤\leq≤ i ≤\leq≤n)后面时,必须从后向前循环,逐个向后移动n-i个表项。 因此,最好的情形是在第n个表项后追加新表项,移动表项个数为0;最差情形是在第1个表项位置插人新表项(视为在第0个表项后面插入),移动表项个数为n;平均数据移动次数AMN(average moving number)在各表项插入概率相等时为
AMN=1n+1∑i=0n(n−i)=1n+1n(n+1)2=n2\frac1 {n+1}\sum_{i=0}^n{(n-i)}=\frac1 {n+1}\frac{n (n+1)} 2=\frac n 2n+11∑i=0n(n−i)=n+112n(n+1)=2n
即就整体性能来说,在插入时有n+1个插入位置,平均移动n2\frac n 22n个表项!
在删除第i个表项(1≤\leq≤ i ≤\leq≤n)时,必须从前向后循环,逐个移动n一i个表项。 因此,最好的情形是删去最后的第n个表项,移动表项个数为0;最差情形是删去第1个表项,移动项个数为n-1;平均数据移动次数AMN在各表项删除概率相等时为
AMN=1n∑i=1n(n−i)=1nn(n−1)2=n−12\frac1 n\sum_{i=1}^n{(n-i)}=\frac1 {n}\frac{n (n-1)} 2=\frac {n-1} 2n1∑i=1n(n−i)=n12n(n−1)=2n−1
就整体性能来说,在删除时有n个删除位置,平均移动(n-1)/2个表项。
以上是C++顺序表使用过程中最常用的主要操作,相关完整代码已经push到GitHub,需要的小伙伴自行clone,如果觉得还不错的话,欢迎Star,这里是传送门C++顺序表,除此之外,想要了解更多的C,C++,Java,Python等相关知识的童鞋,欢迎来我的博客相逢的博客,我们一起讨论!接下来我会更新C语言链式结构实现线性表,敬请期待!