1 线性表介绍
线性表是具有一类相同特性的数据结构的集合。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串……
类似于水果:苹果、梨、香蕉……
逻辑结构指人想象出来的数据的组织形式。线性表在逻辑结构上是线性结构,也就是连续的一条直线。
物理结构是数据在内存上的存储形式。线性表在物理结构上不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
2 顺序表介绍
2.1 概念与结构
顺序表是线性表的一种。如下表,每一格就是一个存储单元。顺序表存储单元的物理地址是连续的,存储单元中依次存储数据元素,在一般情况下用数组存储。可见顺序表的逻辑结构和物理结构都是线性的。
0 | 1 | 2 | 3 | 4 | 5 | 6 |
数组 ———— 顺序表(数组+增加数据+删除数据+修改数据+……)
2.2 分类
2.2.1 静态顺序表
静态顺序表:使用定长数组存储元素。事先无法确定数组大小情况下,静态顺序表会有缺点:给小了空间不够,给大了浪费。
//静态顺序表
typedef int SLDataType;//无法确定数据类型,在此给int起名为SLDataType,便于后续将int修改成char型
#define N 7 //定义N的长度,固定大小为7
typedef struct SeqList{
SLDataType a[N];//定长数组
int size; //有效数据个数
}SL;//顺序表的名字
2.2.2 动态顺序表
动态顺序表:按需申请。
//动态顺序表
typedef struct SeqList //SeqList即顺序表英文表示
{
SLDataType* a;//不知道数组大小,用指针来代替后续可以为指针指向的空间申请空间,多大都可以,动态的申请
int size;//有效数据个数
int capacity;//空间容量
}SL;
动态顺序表空间不够可以用realloc增容。增容一般成倍数增加,常增加两倍。
增容本身就有一定的程序性能消耗,频繁使用会使效率降低,因此不逐个增加,而是成倍数增加。
增容分两种情况:
1.连续空间足够,直接扩容
2.连续空间不够:
(1)重新找一块地址,分配足够的内存;
(2)拷贝数据到新的地址;
(3)销毁旧地址。
3 动态顺序表的实现
3.1 定义动态顺序表的结构
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* arr;
int size;
int capacity;
}SL;
3.2 顺序表的初始化
void SLInit(SL* ps);//顺序表的初始化
void SLInit(SL* ps)//形参,指针接收地址
{
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
3.2 顺序表的销毁
void SLDsetory(SL* ps);//顺序表的销毁
void SLDestory(SL* ps)
{
if (ps->arr) {//判断底层数组是否为空,不为空则释放掉
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
3.3 顺序表的打印
方便后续插入删除时打印顺序表,观察是否正确。
void SLPrint(SL* ps);
void SLPrint(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
3.4 尾插
void SLCheckCapacity(SL* ps);
void SLPushBack(SL* ps,SLDataType x);
void SLCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
int newCapacity = ps->capacity = 0 ? 4 : 2 * ps->capacity;
//增容
SLDataType* temp = (SLDataType*)realloc(ps->arr, newCapacity*sizeof(SLDataType));
if (temp == NULL) {
perror("申请失败!");
exit(1);
}
ps->arr = temp;
ps->capacity = newCapacity;
}
}
void SLPushBack(SL* ps,SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);//判断空间是否足够
ps->arr[ps->size] = x;
ps->size++;
}
顺序表执行插入时,需要判断空间是否足够。足够则直接插入,不足时需要先增容。
3.5 头插
void SLPushFront(SL* ps,SLDataType x);
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[0] = x;
ps->size++;
}
3.6 尾删
void SLPopBack(SL* ps);//尾删
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
ps->size--;
}
删除操作时顺序表内容不能为空。
3.7 头删
void SLPopFront(SL* ps);//头删
void SLPopFront(SL* ps)
{
assert(ps->size);
assert(ps);
for (int i = 0; i < ps->size-1; i++)
{
ps->arr[i] = ps->arr[i+1];
}
ps->size--;
}
3.8 在指定位置之前插入
void SLInsert(SL* ps,SLDataType x,int pos);//指定位置之前插入
void SLInsert(SL* ps,SLDataType x,int pos)//pos为下标
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
for (int i = ps->size-1; i >=pos; i--)/
{
ps->arr[i+1] = ps->arr[i];
}
ps->arr[pos] = x;
ps->size++;
}
3.9 删除指定位置的元素
void SLErase(SL* ps,int pos);
void SLErase(SL* ps,int pos)
{
assert(ps);
assert(ps->size);
assert(pos >= 0 && pos <= ps->size);
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}ps->size--;
}
3.10 查找指定元素
int SLFind(SL* ps,SLDataType x);
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x) {
return i;
}
}
return -1;//没找到
}
3.11 测试函数
void test01()
{
SL s;
SLInit(&s);
SLPushBack(&s, 1);
SLPushBack(&s, 2);
SLPushBack(&s, 3);
SLPushBack(&s, 4);
SLPrint(&s);//打印顺序表,1234
SLPushFront(&s, 5);
SLPushFront(&s, 6);
SLPushFront(&s, 7);
SLPushFront(&s, 8);
SLPrint(&s);//8765 1234
SLPopBack(&s);//8765 123
SLPrint(&s);
SLPopFront(&s);
SLPrint(&s);//765 123
SLInsert(&s,10,0);//相当于头插
SLPrint(&s);//10 765 123
SLInsert(&s, 10, 3);
SLPrint(&s);//10 76 10 5 123
SLErase(&s, 1);//删除了7
SLPrint(&s);//10 6 10 5123
int find = SLFind(&s, 1);
if (find < 0) {
printf("没有这样的数据!\n");
}
else {
printf("有这样的数据\n");
}
}
int main()
{
test01();
return 0;
}