线性表的顺序表示
顺序表使用一组地址连续的存储单元依次存储线性表的数据元素,假设每个数据元素占据k个单元,则顺序表的第i个元素ai的存储位置与前一个元素ai-1和第一个元素的位置关系表达分别如下:
LOC(ai) = LOC(ai-1) + k
LOC(ai) = LOC(a1) + (i-1)*K = LOC(a0) + i*K
其中LOC(a0) 成为线性表的首地址或基地址。
线性表的任意元素可以根据首地址任意存取,因此线性表是一种随机存取的线性表。线性表的存储信息仅有所含有的数据元素,其位置关系是通过其存储结构反映的,因此线性表具有存储密度大、空间利用率高的特点,并且元素可以实现随机存取,但同时线性表插入和删除都需要移动大量元素,一些长度较大的线性表也需要按照其最大需要空间分配存储空间的缺点。
顺序表类的定义
顺序表的随机存取特性与数组相同,因此可以使用数组结构存储顺序表。根据顺序表的特点,可以在线性表抽象类的基础上派生出一个顺序表的抽象类:
template <class type> class SeqList :public ablist<type> { //继承线性表抽象类 protected: type* elements; //指向此存储空间的指针,由于存放线性表 int maxsize; //分配最大空间 public: SeqList(int maxsize = 10); //构造函数,分配内存空间 ~SeqList() { //析构函数,回收空间 if (elements) delete[]elements; } bool Expand(int n); //内存扩充函数 bool Set(type x, int i); //顺序表元素修改 type Get(int i); //获取顺序表指定位置元素 void MakeEmpty() { //清空顺序表 if (elements) delete[]elements; } bool Insert(type x, int i);//在i处插入元素x type Remove(int i); //删除i处元素 type Remove(type x); //删除元素x SeqList<type> & Copy(SeqList<type> &s1); //拷贝函数 SeqList<type> & operator=(SeqList<type> &s1) { //运算符"="重载 Copy(s1); return *this; } friend SeqList<type> & operator<<(ostream&, SeqList<type>&); //运算符"<<"重载 };
顺序表类的实现
构造函数
//构造函数:分配空间 template<class type> SeqList<type>::SeqList(int maxsz) { maxsize = maxsz > 10 ? maxsz : 10; //分配内存空间 length = 0; //元素空间初始化 elements = NULL; elements = new type(SeqList); assert(elements != NULL); //分配成功继续执行,否则报错 }
拷贝函数
template<class type> SeqList<type>& SeqList<type>::Copy(SeqList<type> &s1) { if (elements) //删除本顺序表 delete[]elements; maxsize = 0; length = 0; elements = new type(s1.maxsize);//为顺序表分配空间 if (elements) { maxsize = s1.maxsize; //分配空间大小复制 length = s1.length; //数据空间大小复制 memmove(elements, s1.elements, sizeof(type)*maxsize); } //内容拷贝 return *this; }
动态内存再分配函数
template<class type> bool SeqList<type>::Expand(int newmaxsize) { type *p, *q; if (newmaxsize <= maxsize) { cout << "new maxsize is smaller than current maxsize "; return false; } //新空间大小不能小于原来长度 p = new type(newmaxsize); if (!p) { cout << "cannot expand seqlist "; return false; } //分配失败,显示出错信息 memmove(p, elements, sizeof(type)*length); //内容复制 q = elements; //保存原有地址 elements = p; //指向新的地址 delete[]q; //原有空间释放 maxsize = newmaxsize; return true; }
元素修改函数
template<class type> bool SeqList<type>::Set(type x, int index) { if (index >= maxsize) //超过内存空间无法修改 { cout << "index is out of range "; return false; } for (index > length) //index大于长度时为添加 length++; //此时长度加1 elemnts[index] = x; return true; }
取值函数
template<class type> type SeqList<type>::Get(int index) { assert(index < maxsize); //判断索引值是否超出内存空间 return elements[index]; }
插入函数
template<class type> bool SeqList<type>::Insert(type x, int index) { int j; if (index >= maxsize) { if (Expand(maxsize * 2)) //这句不太理解,为什么是maxsize*2 elements[length] = x; //个人猜测是空间自定义限制 else { cout << "cannot expand seqlist "; return false; } } else if (index >= length) //对于内存空间以内的空位置 elements[length] = x; //直接补在顺序表最后 else { for (j = length; j > idnex; j--) //将插入位置后的元素后移 elements[j] = elements[j - 1]; elements[index] = x; //元素插入 } length++; //长度值加1 return true; }
删除函数1
template<class type> type SeqList<type>::Remove(type value) { int i, index; for (i = 0; i < length; i++) //查找元素value位置 { if (elements[i] == value) break; index = i; } assert(index < length); //查找不到则报错 for (i = index; i < length - 1; i++) elements[i] = elements[i - 1]; //待查元素后继数据前移 length--; //长度减1 return value; }
删除函数2
template<class type> type SeqList<type>::Remove1(int index) { assert(index < maxsize); //判断元素是否属于顺序表空间 //感觉不是很有意义,难道使用是否属于数据空间不更好吗? type value = elements[index]; //获得被删除元素 for (int i = index; i < length - 1; i++) elements[i] = elements[i + 1]; //删除位置后继元素前移 length--; //元素空间长度减1 return value; }
重载输出运算符
template<class type> ostream operator<<(ostream &output, SeqList<type>&s1) { output << " maxsize:" << s1.maxsize << " length:" << s1.length << endl; for (int i = 0; i < s1.length; i++) output << i << ":" << s1.elements[i] << " " << endl; return output; }
用C语言描述:
插入操作:在长度为n的线性表(a1,a2 ,...,an)的第i-1个和第i个元素之间插入新元素。
int InsertSList(int L[],int n,int i,int x) { //n:表长度 i:在位置i处插入 x:插入元素x int j; if (i<0 || i>=n-1)//插入位置非法 printf(" Can't insert! "); else { for (j=n-1;j>=i-1;j--) L[j+1]=L[j];//后移 L[i]=x;//插入 n++;//表长加1 } return(n);//返回表长 }
删除操作:删除第i个元素
int DeleteSList(int L[],int n,int i) { int j; if (i<0 || i>n-1) //删除位置非法 printf(" Can't delete! "); else { for (j=i-1;j<=n-2;j++) L[j]=L[j+1];//前移 n--; //表长减1 } return(n); //返回表长 }
插入时间复杂度(平均)分析:
删除时间复杂度(平均)分析:
因此无论是插入还是删除,平均需要移动线性表中一半的元素,在表长较大时是十分可观的。