目录
实际上就是一种从第一个位置开始向后依次连续存储数据的数组
一.实现结构(两种)
静态:
动态
二.代码实现
使用防御式编程,凡涉及到指针(函数参数中有指针)的地方,全部断言,出现错误后,错误可以被快速清晰的找到.
(一).初始化
注意:
1.为什么参数传指针,形参的改变不影响实参,调试一下
void SLInit(SL* ps)//ps-p-point
{
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
(二).扩容
由于尾插,头插等函数都需要扩容,因此将扩容功能封装成函数,方便使用.
注意:
realloc有两种扩容方式
原地扩容:
异地扩容:旧空间释放,返回新空间的指针
realloc返回值为void*类型
typedef int SLDataType;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity*sizeof(SLDataType));
(由于reallloc返回类型是void*,但要赋值给int*,强转为int*)realloc(要扩容数组的第一个元素的指针,要扩容到多少个字节数)
void SLCheckCapacity(SL* ps)
{
//一个数据都没存储(capacity=0),数据存满(capacity=size)这俩种情况都需要扩容
if (ps->capacity == ps->size)//可以表示俩种情况,0==0,满==满
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//一个数据都没存,给4个字节空间
//有数据存储,但已经存满,给俩倍空间
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity*sizeof(SLDataType));
//为啥不直接给ps->a,因为害怕realloc扩容失败,等扩容成功后,再把tmp赋值给ps->a
if (tmp == NULL)
{
perror("realloc");
exit(-1);//退出程序
}
ps->a = tmp;
ps->capacity = newCapacity;
}
}
(三).尾插
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
(四).头插
把所有数据向后挪动,而且必须从后向前挪动
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end+1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;//标号++
}
(五).尾删
注意:
1.尾删4次,size就会变成-1
数组会越界
因此每次尾删前需要判断size:size>02.并不是每次数组越界编译器都会检查,编译器只会抽查.(这里的数组越界会在销毁空间(free)时被编译器检查到)
void SLPopBack(SL* ps)
{
assert(ps != NULL);
assert(ps->size > 0);///为假,assert会报错
ps->size--;
}
(六).头删
注意
1.和尾删一样,每次头删前也需要判断size(当前空间种数据个数)必须大于0
2.头删必须从前向后把数据向前挪动一格,不能从后向前
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size > 0);///为假,报错
int begin = 0;
int end = ps->size - 1;
while (begin < end)
{
ps->a[begin] = ps->a[begin + 1];
++begin;
}
ps->size--;
}
(七).任意位置插入
注意
1.pos范围[0,size] 为什么可以等于size,尾插时ps->a[ps->size]这个位置也可以插入
2.插入数据都需要调用 SLCheckCapacity(ps);函数
3.pos是要插入的位置(pos指的是数组下标)
void SLInsert(SL* ps, int pos, int x)//pos是数组下标
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
int begin = pos-1;
int end = ps->size;
while (end >= begin)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
代码复用
//头插
void SLPushFront(SL* ps, SLDataType x)
{
SLInsert(ps, 0, x);
}
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
SLInsert(ps, ps->size, x);//代码复用
}
(八).任意位置删除
注意:begin<size-1而不是begin<size
void SLEarse(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
//不用检查数组a是否为空,这里顺带就检查了,pos < ps->size(ps->size为0)
//注意控制边界,控制方式1
//int begin = pos;
//while (begin < ps->size - 1)//小于ps->size - 1而不是小于ps->size,防止数组越界
//{
//ps->a[begin] = ps->a[begin + 1];
//++begin;
//}
//注意控制边界,控制方式2
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
}
代码复用
//头删
void SLPopFront(SL* ps)
{
SLEarse(ps, 0);
}
//尾删
void SLPopBack(SL* ps)
{
SLEarse(ps, ps->size - 1);
}
(八)查找
找到了,返回找到数的下标
找不到,返回-1
int SLSearch(SL* ps, SLDataType x)
{
assert(ps);
int i = 0;
for (i = 0; i < ps->size; ++i)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;//x不在顺序表中
}
(九).修改
void Modify(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
(十).交互使用
SLSearch与Modify或者SLEarse可以交互使用
如果顺序表中有要找的数字,则SLSearch会返回该数字下标
而Modify或者SLEarse需要数字下标才能找到数字进行操作
void TestSeqList8()//测试查找与修改
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
PrintSeqList(&sl);
//SLSearch可以和SLEarse交互使用
//SLSearch如果找到要找的数会返回下标,把下标传给SLEarse这个数就会被删除
int num = 0;
printf("请输入你要删除的值:\n");
scanf("%d", &num);
int pos = SLSearch(&sl, num);//返回你要删除那个值的下标
if (pos != -1)
{
SLEarse(&sl, pos);
PrintSeqList(&sl);
}
else
{
printf("你要删除的值不在顺序表中\n");
}
//SLSearch可以和Modify交互使用
//SLSearch返回下标,将下标传给Modify修改
int y, z;
printf("请输入你要修改的值和修改后的值:");
scanf("%d %d", &y, &z);//输入哪个数字就会修改哪个数字
pos = SLSearch(&sl, y);
if (pos != -1)
{
Modify(&sl, pos, z);
}
else
{
printf("要修改的%d不存在", y);
}
PrintSeqList(&sl);
}
三.完整代码
//SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>//realloc,malloc
#include<assert.h>
//typedef int SLDataType;
静态存储,N太小,空间不够用,N太大,浪费空间
//#define MAX 100
//typedef struct SeqList
//{
// SLDataType a[MAX];//开MAX个数组
// int size;//当前数据个数
//}SL;
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;//动态顺序表首元素指针
int size;//当前存储数据个数
int capacity;//当前存储容量
}SL;
void PrintSeqList(SL* ps);//打印顺序表
void SLInit(SL* ps);//初始化
void SLCheckCapacity(SL* ps);//检查容量
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPushFront(SL* ps, SLDataType x);//头插
void SLPopBack(SL* ps);//尾删
void SLPopFront(SL* ps);//头删
void SLDestory(SL* ps);//销毁空间
void SLInsert(SL* ps, int pos, SLDataType x);//任意位置插入
void SLEarse(SL* ps, int pos);//任意位置删除
int SLSearch(SL* ps, SLDataType x);//查找,返回下标
void Modify(SL* ps, int pos, SLDataType x);//修改数组中下标为pos位置的元素,改为x
//SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void PrintSeqList(SL* ps)
{
int i = 0;
for (i = 0; i < ps->size; ++i)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
void SLInit(SL* ps)//ps-p-point
{
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
//一个数据都没存储(capacity=0),数据存满(capacity=size)这俩种情况都需要扩容
if (ps->capacity == ps->size)//可以表示俩种情况,0==0,满==满
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//一个数据都没存,给4个字节空间
//有数据存储,但已经存满,给俩倍空间
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity*sizeof(SLDataType));
//为啥不直接给ps->a,因为害怕realloc扩容失败,等扩容成功后,再把tmp赋值给ps->a
if (tmp == NULL)
{
perror("realloc");
exit(-1);//退出程序
}
ps->a = tmp;
ps->capacity = newCapacity;
}
}
void SLPushBack(SL* ps, SLDataType x)
{
//assert(ps);
//SLCheckCapacity(ps);
//ps->a[ps->size] = x;
//ps->size++;
SLInsert(ps, ps->size, x);//代码复用
}
void SLPushFront(SL* ps, SLDataType x)
{
//assert(ps);
//SLCheckCapacity(ps);
//int end = ps->size - 1;
//while (end >= 0)
//{
// ps->a[end+1] = ps->a[end];
// --end;
//}
//ps->a[0] = x;
//ps->size++;//标号++
SLInsert(ps, 0, x);
}
void SLPopBack(SL* ps)
{
//assert(ps != NULL);
//assert(ps->size > 0);///为假,报错
//ps->size--;
SLEarse(ps, ps->size - 1);
}
void SLPopFront(SL* ps)
{
//assert(ps);
//assert(ps->size > 0);///为假,报错
//int begin = 0;
//int end = ps->size - 1;
//while (begin < end)
//{
// ps->a[begin] = ps->a[begin + 1];
// ++begin;
//}
//ps->size--;
SLEarse(ps, 0);
}
void SLDestory(SL* ps)
{
assert(ps);
if (ps->a != NULL)
{
free(ps);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
}
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
int begin = pos-1;
int end = ps->size;
while (end >= begin)
{
ps->a[end + 1] = ps->a[end];
//ps->a[end] = ps->a[end-1];
--end;
}
ps->a[pos] = x;
ps->size++;
}
void SLEarse(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
int begin = pos;
int end = ps->size - 1;
while (begin < end)
{
ps->a[begin] = ps->a[begin + 1];
++begin;
}
ps->size--;
}
int SLSearch(SL* ps, SLDataType x)
{
assert(ps);
int i = 0;
for (i = 0; i < ps->size; ++i)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;//x不在顺序表中
}
void Modify(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
//test.c 测试
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void TestSeqList1()
{
SL sl;
SLInit(&sl);
}
void TestSeqList2()//尾插
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
PrintSeqList(&sl);
}
void TestSeqList3()//头插
{
SL sl;
SLInit(&sl);
SLPushFront(&sl, 1);
SLPushFront(&sl, 2);
SLPushFront(&sl, 3);
SLPushFront(&sl, 4);
SLPushFront(&sl, 5);
SLPushFront(&sl, 6);
PrintSeqList(&sl);
}
void TestSeqList4()//尾删
{
SL sl;
SLInit(&sl);
SLPushFront(&sl, 1);
SLPushFront(&sl, 2);
SLPushFront(&sl, 3);
SLPushFront(&sl, 4);
SLPushFront(&sl, 5);
SLPushFront(&sl, 6);
PrintSeqList(&sl);
SLPopBack(&sl);
SLPopBack(&sl);
SLPopBack(&sl);
SLPopBack(&sl);
SLPopBack(&sl);
//SLPopBack(&sl);
PrintSeqList(&sl);
}
void TestSeqList5()//尾删
{
SL sl;
SLInit(&sl);
//SL* sl = NULL;
//SLInit(&sl);
SLPushFront(&sl, 1);
SLPushFront(&sl, 2);
SLPushFront(&sl, 3);
SLPushFront(&sl, 4);
SLPushFront(&sl, 5);
SLPushFront(&sl, 6);
PrintSeqList(&sl);
SLPopFront(&sl);
SLPopFront(&sl);
SLPopFront(&sl);
SLPopFront(&sl);
SLPopFront(&sl);
SLPopFront(&sl);
SLPopFront(&sl);
PrintSeqList(&sl);
}
void TestSeqList6()//任意位置插入
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
PrintSeqList(&sl);
SLInsert(&sl, 3, 208);
PrintSeqList(&sl);
SLPushBack(&sl, 298);
PrintSeqList(&sl);
SLPushFront(&sl, 9998);
PrintSeqList(&sl);
}
void TestSeqList7()//任意位置删除
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
PrintSeqList(&sl);
//SLEarse(&sl, 3);
//PrintSeqList(&sl);
SLPopBack(&sl);
PrintSeqList(&sl);
SLPopFront(&sl);
PrintSeqList(&sl);
}
void TestSeqList8()//测试查找与修改
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
PrintSeqList(&sl);
//SLSearch可以和SLEarse交互使用
//SLSearch如果找到要找的数会返回下标,把下标传给SLEarse这个数就会被删除
int num = 0;
printf("请输入你要删除的值:\n");
scanf("%d", &num);
int pos = SLSearch(&sl, num);//返回你要删除那个值的下标
if (pos != -1)
{
SLEarse(&sl, pos);
PrintSeqList(&sl);
}
else
{
printf("你要删除的值不在顺序表中\n");
}
//SLSearch可以和Modify交互使用
//SLSearch返回下标,将下标传给Modify修改
int y, z;
printf("请输入你要修改的值和修改后的值:");
scanf("%d %d", &y, &z);//输入哪个数字就会修改哪个数字
pos = SLSearch(&sl, y);
if (pos != -1)
{
Modify(&sl, pos, z);
}
else
{
printf("要修改的%d不存在", y);
}
PrintSeqList(&sl);
}
int main()
{
//TestSeqList1();
//TestSeqList2();
//TestSeqList3();
//TestSeqList4();
//TestSeqList5();
//TestSeqList6();
//TestSeqList7();
TestSeqList8();
return 0;
}