线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是?种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的?条直线。
但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。
?顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口
所以可以这么说,顺序表的本质就是一个数组.
静态顺序表因为不常用,这里便不进行介绍了,感兴趣的可自行去了解
重点是动态顺序表
typedef struct seqlist
{
?? ?sldatatype* arr; ?//顺序表本质是个数组,这里定义一个指针arr用于对顺序表进行操作
?? ?int capacity; ? ? ? ?//顺序表可存储的最大元素个数?
?? ?int size; ? ? ? ? ? ? ? ?//size指的是当前顺序表(数组)存储的元素个数
}sl;//sl是此顺序表的类型
举个例子来更好的了解capacity和size,如果说capacity是一个碗,那么size就是这个碗里面盛的饭
void slinit(sl *ps)
{//传值的本质是形参是实参的值的拷贝
?? ?ps->arr = NULL;//此时的结构体变量还没有初始化,所以没有具体的值,所以应该传址
?? ?ps->size = ps->capacity = 0;
}
为什么这里使用的是传址的方式void slinit(sl *ps)
传址的方式更为常用,因为这样可以减少时空复杂度
特别是在需要对结构体变量进行修改操作或者结构体变量较大时,传址方式可以提高程序的效率和可读性。
或者这么说,顺序表的本质是个数组,传参是指针才方便操作嘛
//此函数的作用是判断内存是否足够,是否需要扩容
void slcheckcapacity(sl* ps)
{
?? ?//若空间不够,扩容
? ? //使用三目操作符更简洁:
?? ?if (ps->size == ps->capacity)//当前已使用空间等于最初给的最大容量
?? ?{
?? ??? ?//因为之前是有capacity的初始化为0操作,所以直接2被扩容是无效的,所以这里要对其进行最大容量初始化不为0操作
?? ??? ?int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
?? ??? ?//保证初始空间不为以后创建一个tmp指针变量用realloc函数对ps所指向的空间进行扩容操作
?? ??? ?sldatatype* tmp = (sldatatype*)realloc(ps->arr, newcapacity * sizeof(sldatatype));//对数组arr进行扩容,扩容后的空间大小一般是扩容前的空间二倍大小
?? ??? ?if (tmp == NULL)//扩容失败
?? ??? ?{
?? ??? ??? ?perror("realloc fail");
?? ??? ??? ?exit(1);
?? ??? ?}
?? ??? ?//扩容成功,realloc函数不需要手动free,因为它以经帮你做好了
?? ??? ?ps->arr = tmp;
?? ??? ?ps->capacity = newcapacity;
?? ?}
}
void slpushback(sl* ps, sldatatype x)
{
?? ?assert(ps != NULL);? ??slcheckcapacity(ps);
?? ?//若空间足够,直接插入
?? ?ps->arr[ps->size] = x;//向着数组arr中尾插元素x,size是数组当前的大小
?? ?ps->size++;//有效数据个数随之++
}
3.4顺序表的头插操作
//将头后面的所有数据都向前挪动一位
void slpopfront(sl* ps)
{
?? ?assert(ps);
?? ?assert(ps->size);?? ?for (int i = 0;i<ps->size-1;i++)//最后一个有效数据是ps->arr[size-1]
?? ?{
?? ??? ?ps->arr[i] =ps->arr[i + 1] ;//将ps->arr[i + 1] 的元素赋值给?? ?ps->arr[i]
?? ?}
?? ?ps->size--;
}
3.4顺序表的头插操作
//将所有的数据都往后移一位,使顺序表头部空出一个位置
void slpushfront(sl* ps, sldatatype x)
{
?? ?assert(ps);
?? ?slcheckcapacity(ps);?? ?//旧数据往后挪一位
?
?? ?for (int i = ps->size; i >0; i--)
?? ?{
?? ??? ?ps->arr[i]=ps->arr[i - 1];//将前面的数据ps->arr[i - 1]赋值给后面的数据ps->arr[i]以达到元素后移的目的
?? ?}
?? ?ps->arr[0] = x;
?? ?ps->size++;
?
}
//将所有的数据都往后移一位,使顺序表头部空出一个位置
void slpushfront(sl* ps, sldatatype x)
{
?? ?assert(ps);
?? ?slcheckcapacity(ps);?? ?//旧数据往后挪一位
?
?? ?for (int i = ps->size; i >0; i--)
?? ?{
?? ??? ?ps->arr[i]=ps->arr[i - 1];//将前面的数据ps->arr[i - 1]赋值给后面的数据ps->arr[i]以达到元素后移的目的
?? ?}
?? ?ps->arr[0] = x;
?? ?ps->size++;
}
void slpopback(sl *ps)
{
?? ?//顺序表及其内部数据都不可为空,如果顺序表存储的数据为空,那么它就无法进行删除操作
?? ?//就是capacity和size都不可以为空
?? ?assert(ps);
?? ?assert(ps->size);?? ?//顺序表不为空
?? ?ps->size--;
}
//将头后面的所有数据都向前挪动一位
void slpopfront(sl* ps)
{
?? ?assert(ps);
?? ?assert(ps->size);?? ?for (int i = 0;i<ps->size-1;i++)//最后一个有效数据是ps->arr[size-1]
?? ?{
?? ??? ?ps->arr[i] =ps->arr[i + 1] ;//将ps->arr[i + 1] 的元素赋值给?? ?ps->arr[i]
?? ?}
?? ?ps->size--;
}?
void slinsert(sl* ps, int pos, sldatatype x)//pos是指定位置
{
?? ?assert(ps);
?? ?assert(pos >= 0 && pos <= ps->size);
?? ?slcheckcapacity(ps);?? ?for (int i=ps->size;i>=pos;i++)
?? ?{
?? ??? ?ps->arr[i] = ps->arr[i - 1];//后移操作,将前一个值赋值给后面一个值
?? ?}
?? ?ps->arr[pos] = x;
?? ?ps->size++;
}?
void slerase(sl* ps, int pos)
{
?? ?assert(ps);
?? ?//pos之后的数据前挪一位
?? ?assert(pos >= 0 && pos <= ps->size);
?? ?for (int i=pos;i<ps->size;i++)
?? ?{
?? ??? ?ps->arr[i] = ps->arr[i + 1];
?? ?}
?? ?ps->size--;
}
?
完整代码:?登录 - Gitee.com
?