顺序表是什么呢?
顾名思义,顺序表是物理地址连续的存储单元依次存储数据的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。
顺序表和数组有什么区别呢?
顺序表比数组更约束,顺序表物理地址上必须连续存储,数组是顺序表在实际编程中的具体实现方式。
文章目录
顺序表分为静态顺序表和静态顺序表(主要区别在于他们的容量是否可变)
静态顺序表
静态顺序表的概念
顺序表的容量在静态时期(编译期间)确定,大小不可改变。
静态顺序表的创建
typedef struct SeqList{
int array[100];
int size;//顺序表中实际存储的数据个数
}SeqList;//别名
静态顺序表相对来比较简单,我们这里就不做深入的讨论,接下来主要了解一下动态顺序表。
动态顺序表
动态顺序表的概念
顺序表的容量在动态时期(运行期间)确定,大小可改变。
动态顺序表的创建
typedef struct SeqList{
int* array;//动态从堆上申请
int capacity;//容量
int size;//有效数据的个数 尾插时,可用位置的下标
}SeqList;
动态顺序表的初始化
Main.c
初始化顺序表传参时应该传&seqlist,用结构体指针来接收 SeqList ✳ps,并且还应传顺序表的容量capacity。
SeqList seqlist;
SeqListInit(&seqlist,10);
SeqList.h
void SeqListInit(SeqList *ps,int capacity);
SeqList.c
void SeqListInit(SeqList *ps,int capacity){
ps->array = malloc(sizeof(int) * capacity);//申请空间大小
assert(ps->array != NULL);
ps->size = 0;
ps->capacity = capacity;
}
动态顺序表的销毁
Main.c
这里传参依旧是&seqlist,注意释放顺序表空间free(ps->array),销毁时传参不需要传顺序表的容量。
SeqListDestroy(&seqlist);
SeqList.h
void SeqListDestroy(SeqList *ps);
SeqList.c
void SeqListDestroy(SeqList *ps){
//释放 array 的空间
free(ps->array);
//锦上添花
ps->array = NULL;
ps->size = 0;
ps->capacity = 0;
}
动态顺序表的扩容
当顺序表的容量不够用时,我们应该怎么办呢?当然是扩大顺序表的容量。
具体步骤:
1.当size(实际存储)>=capacity(顺序表容量)时,顺序表执行扩容。
2.扩容一般是两倍增长,新顺序表是原顺序表容量的2倍大小。(缺陷:会造成空间的浪费)int newCapacity = ps->capacity * 2;
3.注意:不是在原顺序表的基础上增加容量,而是新建一个顺序表。int *newArray = (int*)malloc(sizeof(int)*newCapacity);
4.将原来顺序表的数据加载到新的顺序表中。
for (int i = 0; i < ps->sz; i++){ newArray[i] = ps->array[i]; }
5.扩容完成,释放原来顺序表的内存。
free(ps->array);
6.将array的关系指向新的顺序表。
ps->array = newArray;
7.改变capacity
ps->capacity = newCapacity;
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);
void SeqListPushBack(SeqList *ps, int v){
CheckCapacity(ps);
ps->array[ps->size] = v;
ps->size++;
}
头插:
头插可以直接将数据插到顺序表的首位吗?很显然是不行的,因为顺序表首位已经有了数据。
头插时,应从后向前依次将数据向后搬移一位,将首位腾出给目标数据插入。
void SeqListPushFront(SeqList *ps, int v);
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);
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下标删除)
尾删:
直接使顺序表size--
void SeqListPopBack(SeqList *ps);
void SeqListPopBack(SeqList *ps){
assert(ps->size > 0);
ps->size--;
}
头删:
直接前移覆盖第一个数据
void SeqListPopFront(SeqList *ps);
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);
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 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 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);
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);
void SeqListModify(SeqList *ps, int pos, int v){
assert(pos >= 0 && pos < ps->size);
ps->array[pos] = v;
}
到这里顺序表的基本问题就解释完了,相信多多少少会解决大家心头的疑问,在数据结构的学习中应当善于思考,多画图,死磕代码,注意细节,将伪代码转换为代码,这样才能很好的掌握数据结构的有关知识。