一、概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成为数据的增删查改。
顺序表一般可以分为:
1、静态顺序表:使用定长数组存储
2、动态顺序表:使用动态开辟的数值存储
//顺序表的静态存储
#define N 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType array[N];//定长数组
size_t size;//有效数据的个数
}SeqList;
//顺序表的动态存储
typedef struct SeqList
{
SLDataType* array;//指向动态开辟的数组
size_t size;//有效数据个数
size_t capacity;//容量空间的大小
}SeqList;
顺序表的动态存储:
二、 顺序表的短板
1、插入元素,时间复杂度O(n)
插入为第i个元素,则需要移动n-i+1个数据元素,需要移动第n到第i个元素。
均值的计算:一共为(n+1)(n+0)/2,因为一共计算插入了n+1个位置。则均值为:n/2
2、删除元素,时间复杂度 O(n)
删除 第 i 个 元素,则需要移动 n - i 个数据元素 。 需要移动 第 i+ 1 到 第 n 个 元素。
均值的计算:一共为 (n)(n-1+0) / 2 ,因为一共计算删除 n 个位置。则均值为 : (n-1) / 2
三、 malloc、calloc、realloc区分?
相同点:
1、都是C标准库提供动态内存申请的库函数
2、返回值类型都是void*,因此在使用时必须强转
3、申请空间成功,返回值空间的首地址,失败返回NULL,因此在使用时必须判空
4、申请的空间都在堆上,使用完成后必须要使用free进行释放,否则就会存在内存泄漏
不同点:
1、 malloc:参数申请空间大小的字节数,直接将空间申请成功返回给用户
2、calloc:有两个参数—参数1:表示元素个数,参数二:表示元素类型
功能:根据所给的参数来申请空间,并将空间初始化为0
3、realloc:
void* realloc(void* ptr,size_t size):将ptr所指向空间调整到size个字节
- ptr==NULL,realloc函数相当于malloc
- ptr!=NULL,realloc将ptr指向的空间调整到size字节
假设:ptr指向的空间大小为oldsize个字节
size<=oldesize:将ptr指向的空间缩小到size字节,然后返回空间首地址
size>oldsize:将ptr指向的空间扩大到size字节
四、 接口的实现
- 初始化
void SeqListInit(Seqlist* s, int initcapacity)
{
assert(s);
initcapacity = initcapacity <= 3 ? 3 : initcapacity;
s->array = (SDataType*)malloc(sizeof(SDataType*) * initcapacity);
if (NULL == s->array)
return;
s->capacity = initcapacity;
s->size = 0;
}
- 扩容
1、开辟新空间
2、拷贝元素
3、释放旧空间
4、使用新空间
int CheckCapacity(Seqlist* s)
{
assert(s);
if (s->size == s->capacity)
{
int newCapacity = s->capacity * 2;
s->array = (SDataType*)realloc(s->array, newCapacity * sizeof(SDataType));
if (NULL == s->array)
return 0;
s->capacity = newCapacity;
申请新空间
//SDataType* temp = (SDataType*)malloc(newCapacity * sizeof(SDataType));
拷贝元素
//memcpy(temp, s->array, s->capacity * sizeof(SDataType));
释放旧空间
//free(s->array);
使用新空间
//s->array = temp;
//s->capacity = newCapacity;
}
return 1;
}
- 尾插
1、不需要扩容,直接将元素插在size位置
2、需要扩容
void SeqListPushBack(Seqlist* s, SDataType data)
{
assert(s);
//在插入之前要保证有空间
if (!CheckCapacity(s))
return;
s->array[s->size++] =data;
}
- 尾删
void SeqListPopBack(Seqlist* s)
{
assert(s);
if (SeqListEmpty(s))
return;
s->size--;
}
- 首插
1、先检测是否需要扩容–如果需要则扩容
2、将顺序表中所有元素整体往后搬移一个位置
3、将data插入到顺序表的起始位置
void SeqListPushFront(Seqlist* s, SDataType data)
{
if (!CheckCapacity(s))
return;
for (int i = s->size - 1; i >= 0; i--)
{
s->array[i + 1] = s->array[i];
}
s->array[0] = data;
s->size++;
}
- 首删–出第一个元素外,剩余元素整体向前搬移
void SeqListPopFront(Seqlist* s)
{
if (SeqListEmpty(s))
return;
for (int i = 1; i < s->size; ++i)
{
s->array[i - 1] = s->array[i];
}
s->size--;
}
- 任意位置插入
1、pos的范围在[0,size]
2、检测是否需要扩容
3、将[pos,size)位置的元素整体往后搬移一个位置
4、在pos的位置插入新元素
void SeqListInsert(Seqlist* s, int pos, SDataType data)
{
assert(s);
if (pos<0 || pos>s->size)
return;
if (!CheckCapacity(s))
return;
for (int i = s->size - 1; i >= pos; --i)
{
s->array[i + 1] = s->array[i];
}
s->array[pos] = data;
s->size++;
}
- 任意位置删除
1、pos必须在[0,size)
2、将pos之后的所有元素整体往前搬移一个位置
void SeqListErase(Seqlist* s, int pos)
{
assert(s);
if (pos < 0 || pos >= s->size)
return;
for (int i = pos + 1; i < s->size; ++i)
s->array[i-1] = s->array[i];
s->size--;
}
- 获取顺序表中有效元素个数
int SeqListSize(Seqlist* s)
{
assert(s);
return s->size;
}
- 获取顺序表的容量
int SeqListCapacity(Seqlist* s)
{
assert(s);
return s->capacity;
}
- 检测顺序表是否为空
int SeqListEmpty(Seqlist* s)
{
assert(s);
return 0 == s->size;
}
- 查找data是否在顺序表中
int SeqListFind(Seqlist* s, SDataType data)
{
assert(s);
for (int i = 0; i < s->size; ++i)
{
if (data == s->array[i])
{
return i;
}
}
return -1;
}
- 销毁
void SeqListDestroy(Seqlist* s)
{
assert(s);
if (s->array)
{
free(s->array);
s->array = NULL;
s->capacity = 0;
s->size = 0;
}
}
- 清空
void SeqListClear(Seqlist* s)
{
assert(s);
s->size = 0;
}