顺序表是物理地址连续的存储单元依次存储数据的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。
顺序表比数组更约束,顺序表物理地址上必须连续存储,数组是顺序表在实际编程中的具体实现方式。
顺序表分为静态顺序表和动态顺序表,主要区别是他们的容量是否可变。
静态顺序表
静态顺序表的容量在静态时期(编译期间)确定,大小不可改变。
静态顺序表的创建
typedef struct SeqList
{
int array[100];
int size;//顺序表中实际存储的数据个数
}SeqList;//别名
静态顺序表比较简单,主要介绍动态数据表
动态数据表
动态顺序表的容量在动态时期(运行期间)确定,大小可改变。
动态数据表的创建
typedef struct SeqList{
int* array;//动态从堆上申请
int capacity;//容量
int size;//有效数据的个数 尾插时,可用位置的下标
}SeqList;
动态数据表的初始化
初始化顺序表传参时应该传&seqlist,用结构体指针SeqList ✳ps来接收,并且还应传顺序表的容量capacity。
void SeqListInit(SeqList *ps,int capacity){
ps->array = malloc(sizeof(int) * capacity);//申请空间大小
assert(ps->array != NULL);
ps->size = 0;
ps->capacity = capacity;
}
动态顺序表的销毁
这里传参依旧是&seqlist,注意释放顺序表空间free(ps->array),销毁时传参不需要传顺序表的容量。
void SeqListDestroy(SeqList *ps){
free(ps->array);
//释放 array 的空间
ps->array = NULL;
ps->size = 0;
ps->capacity = 0;
//锦上添花
}
动态顺序表的扩容
1.检查是否需要扩容,当size(实际存储)>=capacity(顺序表容量)时,顺序表执行扩容。
2.扩容一般是两倍增长,新顺序表是原顺序表容量的2倍大小。(缺陷:会造成空间的浪费)
3.注意:不是在原顺序表的基础上增加容量,而是新建一个顺序表。
4.将原来顺序表的数据加载到新的顺序表中。
5.扩容完成,释放原来顺序表的内存。
6.将array的关系指向新的顺序表。
7.改变capacity
具体代码如下:
static void CheckCapacity(SeqList *ps)
{
if (ps->size < ps->capacity)
{
return;
}
//需要扩容
int newCapacity = ps->capacity * 2;
int *newArray = (int*)malloc(sizeof(int)*newCapacity);
assert(newArray != NULL);
//搬移
for (int i = 0; i < ps->size; i++)
{
newArray[i] = ps->array[i];
}
//释放老空间
free(ps->array);
ps->array = newArray;
ps->capacity = newCapacity;
}
动态顺序表的插入(头插,尾插,根据pos下标插入)
尾插时,顺序表有效数据的个数就是尾插时可用位置的下标,完成尾插之后,使有效数据个数size++。
void SeqListPushBack(SeqList *ps, int v)
{
CheckCapacity(ps);
ps->array[ps->size] = v;
ps->size++;
}
头插时,应从后向前依次将数据向后搬移一位,将首位腾出给目标数据插入。
void SeqListPushFront(SeqList *ps, int v)
{
CheckCapacity(ps);
//i表示空间下标
for (int i = ps->size; i >= 1; i--)
{
ps->array[i] = ps->array[i - 1];
}
ps->array[0] = v;
ps->size++;
}
根据POS下标插入:
将下标为pos及pos之后的数据向后移动一位,使ps->array[pos]=v。
void SeqListInsert(SeqList *ps, int pos, int v)
{
CheckCapacity(ps);
//pos = 0为头插 pos = size为尾插
assert(pos >= 0 && pos <= ps->size);
//i代表数据的下标
for (int i = ps->size - 1; i >= pos; i--)
{
ps->array[i + 1] = ps->array[i];
}
ps->array[pos] = v;
ps->size++;
}
动态顺序表的删除(头删,尾删,根据pos下标删除)
尾删:
void SeqListPopBack(SeqList *ps)
{
assert(ps->size > 0);
ps->size--;
}
头删:
直接前移覆盖第一个数据
void SeqListPopFront(SeqList *ps)
{
assert(ps->size > 0);
//i代表空间的下标
for (int i = 0; i <= ps->size - 2; i++)
{
ps->array[i] = ps->array[i + 1];
}
ps->size--;
}
根据pos下标删除:
将pos+1及以后的数据依次前移
void SeqListErase(SeqList *ps, int pos){
assert(ps->size > 0);
assert(pos >= 0 && pos < ps->size);
//i代表数据的下标
for (int i = pos + 1; i <= ps->size - 1; i++){
ps->array[i - 1] = ps->array[i];
}
ps->size--;
}
删除第一个遇到的目标元素:
调用查找和根据pos下标删除的函数即可
int SeqListRemove(SeqList *ps, int v){
int pos = SeqListFind(ps, v);
if (pos = -1){
return;
}
SeqListErase(ps, pos);
}
删除所有的目标元素:
定义i 和 j,若没有遇到目标元素,i++,j++,若遇到目标元素i++,j不动,直到i找到下一个不是目标元素的值,将值赋给j下标所在的数据,即可完成替换目标元素
int SeqListRemoveAll(SeqList *ps, int v){
int i, j;
for (i = 0, j = 0; i < ps->size; i++){
if (ps->array[i] != v){
ps->array[j] = ps->array[i];
j++;
}
}
ps->size = j;
}
动态顺序表的查找:
int SeqListFind(SeqList *ps, int v){
for (int i = 0; i < ps->size; i++){
if (ps->array[i] = v){
return i;
}
}
return -1;
}
动态顺序表的更新:
目标数据直接替换pos下标所在数据
void SeqListModify(SeqList *ps, int pos, int v)
{
assert(pos >= 0 && pos < ps->size);
ps->array[pos] = v;
}