虽然之前没接触过线性表的顺序表部分,但是学起来还是感觉比较容易上手的。
线性表简介
线性表主要由顺序表示或链式表示。在实际应用中,常以栈、队列、字符串等特殊形式使用。
顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,称为线性表的顺序存储结构或顺序映像(sequential mapping)。它以“物理位置相邻”来表示线性表中数据元素间的逻辑关系,可随机存取表中任一元素。
链式表示指的是用一组任意的存储单元存储线性表中的数据元素,称为线性表的链式存储结构。它的存储单元可以是连续的,也可以是不连续的。在表示数据元素之间的逻辑关系时,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置),这两部分信息组成数据元素的存储映像,称为结点(node)。它包括两个域;存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域。指针域中存储的信息称为指针或链 。
顺序表
由于线性表中的每个数据元素的类型相同,通常可以用一维数组来实现顺序表。用数组存储顺序表,必须确定数组的长度,即存放线性表的数组空间的长度。
由于要在线性表中进行插入操作,所以数组的长度必须大于线性表的长度。
顺序表为什么是随机存取的?
因为只要确定来存储顺序表的初始地址,计算任意一个元素的存储地址的时间都是相等的,即可以是随机存取的。
为什么要使用模版类?
因为我们每次操作的元素类型不确定,使用模版类的好处就是可以根据即时数据类型而确定类型。那么我们的线性表既可以处理int类型,也可以处理string类型。
固定的类型名换成模版类型参数名T。
在建立类对象时,如果将实际类型指定为int型,编译系统就会用int取代所有的T,如果指定为float型,就用float取代所有的T。这样就能实现“一类多用”。
接下来就开始写顺序表的实现了,具体操作会有解释。上代码
const int maxsize = 1e5;//定义数组的最大长度为1e5个空间
template <class dt>//定义dt为模版类名称
class seqlist{
private :
dt data[maxsize];//存放数据元素的数组
int length;//表示线性表的长度,这个length是一定小于maxsize的
public :
seqlist (){length=0;}//建立空表
seqlist(){dt a[],int n};//建立长度为n的顺序表,此时a[]的类型是模版类类型,表示顺序表的元素是之前没有限制的,跟传输的数据类型有关
~seqlist(){}//空的析构函数
int length(){return length;}//函数比较简单直接类中实现,求线性表的长度
dt get(int i);//查找第i位元素的值,返回类型为类模版类型
int find(dt x)//在线性表中查找与x相等的值的元素序号,返回序号,所以类型为int
void add(int i,dt x)//插入函数,把x插入线性表中第i个位置
dt delete(int i)//删除函数,删除线性表的第i个位置
void print();//打印函数
};
构造函数的实现
实现原理,我们在主函数创建数组,并赋给数组值,然后把数组的元素值交给线性表,在线性表中实现删除,插入,查询操作
//这里主要写的是有参函数的构造,无参直接构造个空表即可
template <class dt>//每个类外函数前面都写上类模版,表示使用类模版
seqlist<dt>::seqlist(dt a[],int n)//构造一个长度为n的线性表
//构造函数无需返回值类型
{
if(n>maxsize)cout<<"越界";//线性表的长度肯定是小于数组的长度,不然没法进行插入操作
for(int i=0;i<n;i++)
data[i]=a[i];//把我们自己写的a数组传入保存线性表元素的数组
length=n;
}
按位查找
实现原理:在线性表中查找第i的位置元素,但是线性表中数据元素定义为类模版类型,所以返回值要是dt类型
template <class dt>
dt seqlist<dt>::get(int i){
//返回类型为dt,即类模版类型,查找复杂度为 O(1)
if(i<1||i>length)cout<<"查找越界";//不在线性表长度的范围内
else return data[i-1];
}
按值查找
实现原理:在线性表中查找与x相等的元素的序号值,所以返回类型是int类型
template <class dt>
int seqlist<dt>::find(dt x){
//为啥参数是dt类型,不是int类型呢?
//是因为我们操作的数据类型是类模版类型
for(int i=0;i<length;i++)
if(data[i]==x)return i+1;
//为什么返回的序号是i+1,而不是i
//是因为我们在线性表里面查找与x相等的值,
//但是线性表的元素开始下标为1,而不是0,所以要加上1
return 0;
}
插入操作
实现原理:把第i个元素到最后一个元素全部移动一个位置,然后把插入的元素放到第i个,然后线性表长度加1,变为n+1,复杂度为O(n)
template <class dt>
void seqlist<dt>::add(int i,dt x){
if(length >= maxsize)cout<<"越界";
if(i<1||i>length+1)cout<<"下标位置";//说明你要插入的元素的位置不在线性表的长度内
//就直接插入,无需改变线性表元素的位置
for(int j=length;j>=i;j--)
data[j]=data[j-1];//表示在线性表中,第i个元素后面的元素全部往后移一个位置
//把前一个位置的元素赋给后一个元素
data[i-1]=x;
length++;
}
**ps:**在第i个位置上插入一个元素后移动的次数为n-i+1次
删除操作
实现原理:用x暂存要删除的元素值,然后从i个位置的元素全部往前移动一个位置
template <class dt>
dt seqlist <dt>::delete (int i){
dt x;
if(length==0)cout<<"不存在";//空表是无法删除的
if(i<1||i>length)cout<<"元素位置";//表示删除的元素不在线性表的长度范围内,可以直接直接操作
x=data[i-1];//用x暂存data[i-1]的元素
for(int j=i;j<length;j++)
data[j-1]=data[j];
length--;
return x;
}
遍历打印
实现原理:直接遍历打印就ok
template <class dt>
void seqlist <dt>::print(){
for(int i=0;i<length;i++)
cout<<data[i];
}
关于系统如何为静态的顺序表分配内存?
根据我们开辟的数据长度,和数据的类型来分配内存,如果数据类型是类模版的类型,就根据类模版类型的数据部分来分配内存。
容易混淆的点:
线性表的元素下标从1 开始,长度为length
而数组的元素的下标从 0 开始,长度为maxsize
而删除,插入,查询的操作都在线性表内完成
简单的主函数
//这里只写伪代码
int main(){
//比如传入的是数字,可以创建一个int数组;
int a[10005]={1,2,3,4....}
seqlist <int> b(a,105);
//这里创建了一个为b的对象,而b的操作类型为int
b.add(2,5);//这里可以直接根据对象调用类的函数,诸如此类
//。。。。
}