线性表的顺序存储结构(简称为顺序表)是把线性表中的所有元素按照逻辑顺序依次存储到从计算机存储器中指定存储位置开始的一块连续的存储空间中,如下图所示
因此可以根据以上特点定义一个相关的结构体
typedef int DataType;
struct seqList
{//有3个数据成员
int MAXNUM;//用于记录顺序线性表中能存放的最大元素个数的整型 MAXNUM
int curNum;//用于存放顺序线性表中数据元素的个数整型 curNum
DataType *element;//用于存放顺序线性表数据元素的连续空间的起始地址
};
创建一个空的顺序表
创建顺序表要做的是声明一片空间
//创建一个空的顺序线性表,能存放的最大元素个数为 m
PseqList createNullList_seq(int m)
{
//若m=0,则返回NULL
if(m==0){
return NULL;
}
PseqList list=(PseqList)malloc(sizeof(struct seqList));
if(list==0){
return NULL;
}
list->element=(DataType*)malloc(sizeof(DataType));
if(list->element==0){
free(list);
return NULL;
}
list->MAXNUM=m;
list->curNum=0;
return list;
}
判断顺序表是否已满
判断顺序表是否满,实质上是观察顺序表中的数据元素个数是否等于创建顺序表时最大的元素个数
//判断顺序线性表是否已满,若已满,返回值为1,否则返回值为0
int isFullList_seq(PseqList L)
{
if(L==NULL){
return 0;
}
return L->curNum==L->MAXNUM;
}
在顺序表的某个位置插入某个元素
插入元素的思维和排队差不多,如果在队伍的中间插入一个人,前面的人无法再往前移动,所以只能后面的人往后移动一个位置。
// 在线性表L中下标为p的位置插入数据元素x,若下标p非法或线性表已满无法插入数据,返回0;插入成功返回值为1
//如果线性表满了, 还需输"list is full"的提示
//如果插入位置非法,需输出提示"position is illegel"
int insertP_seq(PseqList L , int p ,int x)
{
if (L == NULL) {
return 0;
}
if (isFullList_seq(L)) {
printf("list is full");
return 0;
}
if (p < 0 || p > L->curNum) {
printf("position is illegel");
return 0;
}
for(int i=L->curNum;i>p;i--){
L->element[i]=L->element[i-1];
}
L->element[p]=x;
L->curNum++;
return 1;
}
输出顺序表的元素
遍历整个顺序表,依次将顺序表中的元素输出出来
void printList_seq(PseqList L)
{
for(int i=0;i<L->curNum;i++){
printf("%d ",L->element[i]);
}
}
销毁顺序表
前面创建顺序表是为其声明一块地址空间,拿销毁顺序表的话就是释放掉这一块空间
int destroyList_seq(PseqList L)
{
if(L==NULL){
return 0;
}
int num=L->curNum;
free(L->element);
free(L);
return num;
}
查找顺序表中的元素
常见的有两种查找方式:按地址找和按元素是否相同找,顺序表的查找都是需要遍历整个顺序表
//在顺序表L中查找给定值x首次出现的位置,若不存在给定值,则返回-1
int locate_seq(PseqList L,int x)
{
int i=0;
while(i<L->curNum&&L->element[i]!=x){
i++;
}
if(i==L->curNum){
return -1;
}
return i;
}
// 在顺序表L中查找指定位置pos处的数据元素,若位置非法,则返回第0个数据元素
DataType locatePos_seq(PseqList L,int pos)
{
if(pos>=L->curNum||pos<0){
return L->element[0];
}
return L->element[pos];
}
删除顺序表中的元素
前面说到在顺序表中插入元素是从右往左的,那删除顺序表中的元素应该是相反的,思路还是一样的
int deletePos_seq(PseqList L, int pos)
{
if (L == NULL || pos < 0 || pos >= L->curNum)
{
return -1;
}
// 将删除位置之后的元素依次前移
for (int i = pos; i < L->curNum - 1; i++)
{
L->element[i] = L->element[i + 1];
}
// 元素个数减1
L->curNum--;
return 1;
}
替换顺序表中的值
遍历顺序表,将顺序表中和x相同的值都替换为y
void replace_seq(PseqList L,int x,int y)
{
for(int i=0;i<L->curNum;i++){
if(L->element[i]==x){
L->element[i]=y;
}
}
}
删除顺序表中所有相同的值
void delDuplicate_seq(PseqList L)
{
int same=0;
for(int i=0;i<L->curNum-1;i++){
for(int j=i+1;j<L->curNum;j++){
if(L->element[j]==L->element[i]){
deletePos_seq(L, j);
same=1;
}
}
//前面只是对第二个一样的值进行删除,对自己不删除
if(same){
deletePos_seq(L, i);
same=0;
}
}
}
顺序表的优劣
优点
随机访问效率高:顺序表中的元素在内存中是连续存储的,因此可以通过元素的下标直接访问任意元素,时间复杂度为 (O(1))。这使得对于需要频繁随机访问元素的操作,如查找特定位置的元素、修改元素值等,顺序表具有很高的效率。
存储密度高:顺序表不需要额外的指针来链接元素,每个元素只需要存储其自身的数据,因此存储密度较高,能够有效地利用内存空间。
实现简单:顺序表的实现相对简单,不需要复杂的指针操作和内存管理。它可以使用数组来实现,对数组的操作比较直观和容易理解,因此在一些简单的应用场景中,顺序表是一种很好的选择。
缺点
插入和删除操作效率低:在顺序表中插入或删除元素时,需要移动大量的元素来保持元素的连续性。例如,在表头插入一个元素,需要将后面的所有元素都向后移动一位,时间复杂度为 (O(n)),其中 n 是顺序表中元素的个数。同样,在表尾删除一个元素的效率较高,但在其他位置删除元素也需要移动大量元素。
大小固定,不够灵活:顺序表的大小通常在创建时就已经确定,一旦确定后,很难在不重新分配内存的情况下改变其大小。如果需要存储的元素数量超过了顺序表的初始大小,就需要重新分配更大的内存空间,并将原来的元素复制到新的空间中,这可能会带来较大的性能开销。
不适合频繁的动态操作:由于插入和删除操作的效率较低,顺序表不适合频繁进行动态插入和删除元素的场景。例如,在实时数据处理中,如果需要频繁地添加和删除数据,使用顺序表可能会导致性能下降。