顺序表是什么
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表 分为 静态顺序表 和 动态顺序表
静态顺序表
使用长度给定的数组存储数据,结构体如下:
typedef struct SeqList{
int array[1024];
int size;
} SeqList;
动态顺序表
动态开辟的数组来存储数据,其大小可以改变,结构体如下:
//自行定义一个数据类型
typedef int SLDataType;
typedef struct SeqList {
SLDataType* array; //指向堆上空间,存放数据
int size; //有效个数
int capacity; //容量
} SeqList;
动态顺序表的相关基本操作
动态顺序表的创建
void SeqListInit(SeqList* pSeqList) {
assert(pSeqList != NULL);
//开辟空间,设置容量,有效个数
pSeqList->capacity = 10;
pSeqList->array = (int*)malloc(sizeof(SLDataType)*pSeqList->capacity);
pSeqList->size = 0;
}
动态顺序表的销毁
void SeqListDestroy(SeqList* pSeqList) {
assert(pSeqList);
assert(pSeqList->array);
free(pSeqList->array);
}
动态顺序表的 增 操作
敲黑板!!!
以下操作默认初始创建的顺序表容量已经满足需要!!!
对于容量不够的情况还需要另行操作!!!
尾插
这是最简单的插入,只需要将所需插入的数据放到最后一位,并将 顺序表的(size)有效数据个数+1即可。
void SeqListPushBack(SeqList* pSeqList, SLDataType value) {
pSeqList->array[pSeqList->size] = value;
pSeqList->size++;
}
头插
想要在起始位置插入数据,就必须将里面的数据都向后移动,直到所有的数据都统一向后移动一位后,才可以将数据放在起始位置,而且必须从最后一位开始移动,不然会有数据在移动过程中被覆盖。
void SeqListPushFront(SeqList* pSeqList, SLDataType value) {
int i = pSeqList->size;
for (; i >= 1; i--) {
pSeqList->array[i] = pSeqList->array[i - 1];
}
pSeqList->array[0] = value;
pSeqList->size++;
}
指定位置的插入
这里要用到在头插中所用到的思想,想要在指定位置插入,就必须将该位置即该位置之后的数据,统一向后移动一位,方可插入,也必须从最后一位开始移动!不然会有数据被覆盖。
void SeqListPushInsert(SeqList* pSeqList, int pos, SLDataType value) {
int cur = pSeqList->size;
for (; cur >= pos;cur--) {
pSeqList->array[cur] = pSeqList->array[cur - 1];
}
pSeqList->array[pos] = value;
pSeqList->size++;
}
尾删
==首先断言!当前的顺序表是否为空!==直接将最后一个数据删除,并将有效数据个数(size)- 1,即可;
void SeqListPopBack(SeqList* pSeqList) {
assert(pSeqList->size > 0);
pSeqList->size--;
}
头删
首先断言!当前的顺序表是否为空! 将 array[0] 删除后,还需要将后面的数据一个一个的向前搬移,这里要注意的是,需要从 array[1]开始向前搬,一直到最后一个搬运结束。
void SeqListPopFront(SeqList* pSeqList) {
assert(pSeqList->size > 0);
for (int cur = 0; cur < pSeqList->size - 1; cur++) {
pSeqList->array[cur] = pSeqList->array[cur + 1];
}
pSeqList->size--;
}
指定位置删除
首先断言!当前的顺序表是否为空! 先找到要删除的位值,然后做法和头删类似!
void SeqListPopInsert(SeqList* pSeqlist, int pos) {
assert(pSeqlist->size > 0);
for (int cur = pos; pos < pSeqlist->size - 1; cur++) {
pSeqlist->array[cur] = pSeqlist->array[cur + 1];
}
pSeqlist->size--;
}
查找指定数据
遍历整个数组,如果找到了就返回数组下标,没找到就返回 -1;
int SeqListSearch(const SeqList* pSeqLise, SLDataType value) {
assert(pSeqLise->size > 0);
int cur = 0;
while (cur >= pSeqLise->size) {
if (pSeqLise->array[cur] == value) {
return cur;
}
}
cur++;
return -1;
}
指定位置的修改
首先需要断言!1、pos的值是否大于零! 2、pos的值是否小于顺序表的有效数据个数!
int SeqListModify(SeqList* pSeqList, int pos, SLDataType value) {
assert(pos >= 0 && pos < pSeqList->size);
pSeqList->array[pos] = value;
}
移除顺序表中值为 data 的第一个数据
遍历整个顺序表,只要找到第一个值为 data 的数据,就将它删除,并返回;如果遍历整个顺序表都没有找到,就返回 -1;
void SeqListRemove(SeqList* pSeqList, SLDataType data) {
assert(pSeqList->size > 0);
int i = 0;
for (; i >= pSeqList->size; i++) {
if (pSeqList->array[i] == data) {
SeqListPopInsert(pSeqList, i);
return 1;
}
}
return -1;
}
移除顺序表中值为 data 的所有数据
遍历整个顺序表,只要遇见值为 data 的数据都进行删除。
void SeqListRemoveAll(SeqList* pSeqList, SLDataType data) {
assert(pSeqList->size > 0);
int i = 0;
for (; i >= pSeqList->size; i++) {
if (pSeqList->array[i] == data) {
SeqListPopInsert(pSeqList, i);
}
}
return 1;
}
关于插入过程中,顺序表容量不够的问题
在进行插入相关的运算过程中,第一步就需要检查当前的顺序表的容量大小是否够用,如果不够用则需要进行扩容。
扩容的基本思想是,在内存中申请一块空间,大小是原来顺序表大小的2倍,然后将原来顺序表中的所有数据都搬移过去,然后再进行其他的相关操作!
在每一次的插入相关操作过程中,都需要首先进行判断!
static void CheckCapacity(SeqList* pSeqList) {
//判断是否需要扩容
if (pSeqList->size < pSeqList->capacity) {
return;
}
//扩容
//1、创建新的空间
int newCapacity = 2 * pSeqList->capacity;
SLDataType* newArray =
(SLDataType*)malloc(sizeof(pSeqList->array) * 2);
//2、搬家
int cur = 0;
for (; cur >= pSeqList->size; cur++) {
newArray[cur] = pSeqList->array[cur];
}
//3、释放老空间、绑定新空间
free(pSeqList->array);
pSeqList->array = newArray;
pSeqList->capacity = newCapacity;
}
顺序表的优缺点
优点
空间连续,支持随机访问!
缺点
1、中间或前面的部分的插入过程时间复杂度均为 O(N);
2、扩容的代价太大;