一、线性表的顺序存储结构
顺序存储结构是存储结构中的一种,顺序表首先是一种线性表,在此基础上要求逻辑结构上相邻的数据元素其物理存储结构也相邻,且表中的结点依次存放在计算机内存中一组地址连续的存储单元中,之间不能存在空隙,如下图所示:
将表中元素一个接一个存入一组连续的存储单元中,这种存储结构就是顺序结构。
只要确定了起始位置,表中任意的元素都可以通过下列公式得到: 我们可以轻松访问到顺序表中的所有数据。
二、顺序表的实现
顺序表的实现使用的是数组,这是根据其存储结构所确定的。
(1)顺序存储结构的建立
#define SEQ_INIT_SIZE 10
#define SEQ_INC_SIZE 2
typedef int ElemType;
typedef struct
{
ElemType *data;
int maxsize;
int cursize;
}SeqList;
定义一个结构体,其中包含三个重要属性,数据值data,线性表的最大存储容量maxsize,当前线性表的长度cursize。这三个属性可以描述顺序存储结构。 两条宏定义语句方便顺序表满时动态扩容。
(2)顺序表的初始化
void Init_SeqList(SeqList &tlist)
{
tlist.data = (ElemType*)malloc(sizeof(ElemType)*SEQ_INIT_SIZE);
if(tlist.data == NULL) exit(1);
tlist.maxsize = SEQ_INIT_SIZE;
tlist.cursize = 0;
}
顺序表的初始化就是申请空间,再更改结构体里的属性值。如果申请不到空间就直接退出。
(3)基本信息的获取与判断
int GetCapacity(SeqList &tlist)
{
return tlist.maxsize;
}
int GetSize(SeqList &tlist)
{
return tlist.cursize;
}
bool Is_Empty(SeqList &tlist)
{
return GetSize(tlist) == 0;
}
bool Is_Full(SeqList &tlist)
{
return GetSize(tlist) == GetCapacity(tlist);
}
包括顺序表当前最大存储容量,存储元素个数,以及判空和判满。
(4)增容函数
bool Inc_Capa(SeqList &tlist)
{
ElemType *newdata = (ElemType*)realloc(tlist.data,sizeof(ElemType)*tlist.maxsize*SEQ_INC_SIZE);
if(NULL == newdata) return false;
tlist.data = newdata;
tlist.maxsize *= SEQ_INC_SIZE;
return true;
}
当顺序表满时进行动态增容工作,newdata申请两倍顺序表最大容量的空间,申请失败就返回false;修改顺序表最大容量值为原来的两倍。
(5)按位置插入
bool Insert_Pos(SeqList &tlist,int pos,ElemType val)
{
if(pos < 0 || pos > tlist.cursize) return false;
if(Is_Full(tlist) && !Inc_Capa(tlist))
{
return false;
}
for(int i = tlist.cursize; i> pos; --i)
{
tlist.data[i] = tlist.data[i-1];
}
tlist.data[pos] = val;
tlist.cursize +=1;
return true;
}
在pos位置插入值为val的元素,先要判断要插入的位置是否合法,不合法的话返回false;如果顺序表已满,调用扩容函数,扩容失败的话也返回false。接下来就是从最后一个元素开始,将数组元素依次后移,在pos位放入val值,不能忘记顺序表长度要加1。
(6)按位置删除
bool Erase_Pos(SeqList &tlist,int pos)
{
if(pos < 0 || pos > tlist.cursize-1) return false;
tlist.cursize-=1;
for(int i = pos ; i < tlist.cursize;++i)
{
tlist.data[i] = tlist.data[i+1];
}
return true;
}
删除pos位置的元素,和插入操作相同,先判断删除位置的合法性;将pos之后的元素依次前移,删除元素后,顺序表表长减1。
(7)按值查找
int FindValue(SeqList &tlist,ElemType val)
{
int pos = -1;
for(int i = 0;i<tlist.cursize;++i)
{
if(tlist.data[i] == val)
{
pos = i;
break;
}
}
return pos;
}
在顺序表中查找值为val的位置,返回位置的下标;如果返回-1说明顺序表中没有值为val的元素。
(8)按值删除
void Remove(SeqList &tlist,ElemType val)
{
Erase_Pos(tlist,FindValue(tlist,val));
}
void Remove_All(SeqList &tlist,ElemType val)
{
int j = 0;
for(int i = 0;i<tlist.cursize;++i)
{
if(val != tlist.data[i])
{
tlist.data[j] = tlist.data[i];
++j;
}
}
tlist.cursize = j;
}
第一个函数删除第一个值为val的函数,借助查找函数和按位置删除函数可以很容易实现;第二个函数删除所有值为val的函数,这时需要一点小技巧,借助i,j 降低时间复杂度。
(9)打印顺序表
void Print_SeqList(SeqList &tlist)
{
printf("Capacity: %d \n",tlist.maxsize);
printf("size : %d \n",tlist.cursize);
for(int i = 0;i<tlist.cursize;++i)
{
printf("%d ",tlist.data[i]);
}
printf("\n");
}
三、总结
顺序存储结构的主要优点是节省存储空间,分配给数据的存储空间是连续的且仅用于存放数据,没有占用额外空间;在进行查找等操作时很方便。顺序表的缺点也很明显,在进行插入删除等操作时,再某些情况下移动数据元素的次数过多,不便于修改。