(1)线性表是具有n个相同特性的数据元素的有限序列。是一种在实际中广泛应用的数据结构,常见的线性表:顺序表,链表,栈,队列,字符串…
(2)线性表在逻辑上是线性结构,也就是连续的一条直线。但在物理结构上并不一定是连续的。线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
要求:(1)物理地址连续(2)存储单元连续(3)数据元素的存储连续,不能跳跃式的存储(4)一般采用数组存储,在数组上完成增删查改等功能
(1)静态顺序表:使用定长数组存储元素。
#pragma once//防止头文件重复定义
#define N 10//宏定义
typedef int SLDataType;
typedef struct SeqList
{
SLDataType a[N];
int Size;//存储数据的个数
}SL;
(2) 动态顺序表:使用动态开辟的数组存储。
typedef int SLDataType;//对数据类型进行重命名
typedef struct SeqList
{
SLDataType* a;
int Size;//存储数据的个数
int capacity;//存储空间的大小
}SL;
一般来说,在要扩容的时候,realloc两倍的空间,原因:扩容多了,存在空间浪费,扩容少了,会造成频繁需要再次扩容,效率损失。
typedef int SLDataType;
// 顺序表的动态存储
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
// 基本增删查改接口
// 顺序表初始化
void SeqListInit(SeqList* psl);
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl);
// 顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
// 顺序表尾删
void SeqListPopBack(SeqList* psl);
// 顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);
// 顺序表头删
void SeqListPopFront(SeqList* psl);
// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
// 顺序表销毁
void SeqListDestory(SeqList* psl);
// 顺序表打印
void SeqListPrint(SeqList* psl);
void menu()
{
printf("1.头插 2.尾插 3.头删 4.尾删 5.查找 6.删除 7.插入 8.修改 9.打印 0.退出\n");
}
//菜单
int main()
{
int input = 0;
//定义结构体变量,并初始化
SL s;
SLInit(&s);
int x = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入你要头插的数字,输入-1结束:");
scanf("%d", &x);
while (x != -1)
{
SLPushFront(&s, x);
scanf("%d", &x);
}
break;
case 2:
printf("请输入你要尾插的数字,输入-1结束:");
scanf("%d", &x);
while (x != -1)
{
SLPushBack(&s, x);
scanf("%d", &x);
}
break;
case 3:
SLPopFront(&s);
printf("头部数据已删除\n");
break;
case 4:
SLPopBack(&s);
printf("尾部数据已删除\n");
break;
case 5:
printf("请输入你要查找的数字,输入-1结束:");
scanf("%d", &x);
while (x != -1)
{
int pos=SLFind(&s, x);
if (pos != -1)
{
printf("你要查找的数字的下标是:%d\n", pos);
}
else
{
printf("找不到\n");
}
scanf("%d", &x);
}
break;
case 6:
printf("请输入你要删除的数字,输入-1结束:");
int x = 0;
scanf("%d", &x);
while (x != -1)
{
int pos = SLFind(&s, x);//要先找到数字的下标,才能进行删除
SLDelete(&s, pos);
scanf("%d", &x);
}
break;
case 7:
printf("请输入你要在哪个位置插入数字,输入-1结束:");
x = 0;
scanf("%d", &x);
int pos = SLFind(&s, x);//要先找到数字的下标,才能进行删除
while (pos != -1)
{
int a;
printf("输入你要插入的数字:");
scanf("%d", &a);
SLInsert(&s, pos, a);
scanf("%d", &x);
pos = SLFind(&s, x);//要先找到数字的下标,才能进行删除
}
break;
case 8:
printf("请输入你修改的数字,输入-1结束:");
x = 0;
scanf("%d", &x);
pos = SLFind(&s, x);//要先找到数字的下标,才能进行删除
while (pos != -1)
{
int a;
printf("输入你要修改的数字:");
scanf("%d", &a);
SLModify(&s, pos, a);
scanf("%d", &x);
pos = SLFind(&s, x);//要先找到数字的下标,才能进行删除
}
break;
case 9:
SLPrint(&s);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("输入不合法\n");
break;
}
} while (input);
SLDestory(&s);
return 0;
}
void SeqListInit(SeqList* psl)
{
assert(psl != NULL); //断言
psl->a = NULL; //初始顺序表为空
psl->size = 0; //初始数据个数为0
psl->capacity = 0; //初始空间容量为0
}
void SLDestory(SL* psl)//顺序表销毁
{
assert(psl);
free(psl->a);
psl->a = NULL;
psl->capacity = 0;
psl->Size = 0;
}
void SLPushBack(SL* psl, SLDataType x)//尾插
{
assert(psl);
//检查容量:有效数字和容量相等的话就要扩容
if (psl->Size == psl->capacity)
{
int newCapcity = psl->capacity == 0 ? 4 : psl->capacity * 2;
//我们刚开始将psl->初始化为NULL,所以realloc(NULL,newSize)相当于malloc
SLDataType* p=realloc(psl->a, newCapcity * sizeof(SLDataType));//注意乘数据大小
if (p == NULL)
{
perror("realloc fail\n");
return;
}
//1.原地开辟 2.异地开辟
psl->a = p;//如果异地开辟了,原来的空间会自动回收给操作系统
psl->capacity = newCapcity;
}
psl->a[psl->Size] = x;
psl->Size++;
}
void SLPushFront(SL* psl, SLDataType x)//头插
{
assert(psl);
//检查容量
SLCheckCapacity(psl);
//挪动数据:
int end = psl->Size - 1;
while (end >= 0)
{
psl->a[end + 1] = psl->a[end];
end--;
}
psl->a[0] = x;
psl ->Size++;//插入一个数字,有效数字Size+1
}
void SLPopBack(SL* psl)//尾删
{
//如果没有有效数字,Size为0了
//就不能再进行尾删
assert(psl);
//温柔的检查
//但Size为0,
if (psl->Size == 0)
{
return;//不做任何修改
}
//暴力的检查
//assert(psl->Size > 0);
//Size--有效数字减1,但动态开辟的空间还是在那,没有回收
//有效数字减1,
psl->Size--;
}
void SLPopFront(SL* psl)//头删
{
assert(psl);
//检查有效数字是否已经为0
if(psl->Size==0)
{
return;//不做修改
}
int begin = 0;
while (begin < psl->Size - 1)
{
psl->a[begin] = psl->a[begin + 1];
begin++;
}
--psl->Size;
}
int SLFind(SL* psl, SLDataType x)//顺序表查找
{
assert(psl);
for (int i = 0; i < psl->Size; i++)
{
if (psl->a[i] == x)
{
return i;
}
}
return -1;
}
void SLInsert(SL* psl, size_t pos, SLDataType x)//插入顺序表
{
assert(psl);
assert(pos<=psl->Size);//等于时就相当于尾插
SLCheckCapacity(psl);
//挪动数据
size_t end = psl->Size;
while (end > pos)
{
psl->a[end] = psl->a[end-1];
end--;
}
psl->a[pos] = x;
psl->Size++;
}
顺序表插入这个地方,我们要注意一个问题:end的类型,应该是size_t无符号整型类型,还是int类型。如果我们是在坐标为0的pos,插入数据,相当于头插,那么经过调试会发现程序陷入了死循环,因为在最后一步end–,end会变成负数,而end又是无符号整数,所以这是一个超大的整数,又会进入循环,这样就陷入了死循环。
解决方法:(1)psl->a[end]=psl->a[end-1],且while(end>pos);而不是while(end>=pos) psl->a[end+1]=psl->a[end]
(2)int end,且把pos强制类型转换成(int),如果不把pos强制类型转换成int,两者的类型不同,可能会发生隐式类型转换。
void SLDelete(SL* psl, size_t pos, SLDataType x)//顺序表删除
{
assert(psl);
assert(pos < psl->Size);//删除数据的下标要小于有效数字个数
size_t begin = pos;
while (begin<psl->Size-1)
{
psl->a[begin] = psl->a[begin + 1];
begin++;
}
psl->Size--;
}
有人删除顺序表之后,我们可以把上面的头删和尾删,改成
头删: SLDelete(psl,psl->Size-1);
尾删: SLDelete(psl,0);
void SLModify(SL* psl, size_t pos, SLDataType x)//修改顺序表
{
assert(psl);
assert(pos < psl->Size);
psl->a[pos] = x;
}