静态顺序表
// 静态顺序表 -- 不太实用
// N小了不够用,N大了可能浪费
#define N 10000
typedef struct shunxubiao
typedef int SLDataType;
typedef struct SeqList
{
SLDataType a[N];
int size; // 记录存储多少个有效数据
}SL;
// STL命名
//void SeqListInit(SL s);
void SLInit(SL s);
void SLPushBack(SL s, SLDataType x);
这种静态顺序表不是很实用,因为无法准确获知数据存储的准确数据而可能导致浪费。
在实际应用中,动态顺序表更加实用。
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size; // 记录存储多少个有效数据
int capacity; // 空间容量大小
}SL;
void SLPrint(SL* ps);
void SLInit(SL* ps);
void SLDestroy(SL* ps);
// 尾插尾删
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
因为形参是实参的拷贝,所以用指针传参。
其中的尾插函数涉及到动态内存分布:
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
// 扩容
if (ps->size == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity*sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
ps->a[ps->size] = x;
ps->size++;
}
其中的realloc函数,当指针为空时,其作用相当于malloc。
realloc函数扩容分两种:原地扩容,异地扩容。
原地扩容是指要扩容的空间中没有被使用,就在原空间之后扩容,返回原空间的地址。
异地扩容指的是要扩容的空间里有被使用的情况,这种情况下就会把原空间的内容拷贝到一个没有被使用的新空间里,然后返回新空间的地址。
头插头删
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 SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size > 0);
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
SLErase(ps, 0);
}
其中,如果头插入了4个数据,但头删除调用的次数不止4次,因为可能在destroy中的free函数中报错,原因是,free函数只允许从头到尾地释放开辟的空间,如果指针所指的地址不是开辟空间的最开始就会报错。(free函数会检查是否发生了越界)
一般来讲,越界读一般不会检查出来,但越界写可能会被检查出来。查越界一般会在数组末端后的一两个元素进行检查。(不同编译器也不同)
void SLinsert(SL* ps, int pos, Seqdatatype a)
{
assert(ps);
assert(pos >= 0);
assert(pos <= ps->size);
check(ps);
int end = ps->size - 1;
while (pos <= end)
{
ps->x[end + 1] =ps-> x[end];
end--;
}
ps->x[pos] = a;
ps->size++;
}
void SLerase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0);
assert(pos < ps->size);
int begin = pos + 1;
while (begin < ps->size)
{
ps->x[begin - 1] = ps->x[begin];
begin++;
}
ps->size--;
}
以上为指定位置插入或删除,实际上可以直接代替头插头删,尾插尾删。
菜单:
void menu()
{
printf("***********************************************\n");
printf("1、尾插数据 2、尾删数据\n");
printf("3、头插数据 4、头删数据\n");
printf("5、打印数据 -1、退出\n");
printf("***********************************************\n");
}
int main()
{
SL s;
SLInit(&s);
int option = 0;
int val = 0;
do
{
menu();
printf("请输入你的操作:>");
scanf("%d", &option);
switch (option)
{
case 1:
printf("请依次输入你要尾插的数据,以-1结束");
scanf("%d", &val);
while (val != -1)
{
SLPushBack(&s, val);
scanf("%d", &val);
}
break;
case 2:
SLPopBack(&s);
break;
case 3:
break;
case 4:
break;
case 5:
SLPrint(&s);
break;
default:
break;
}
} while (option != -1);
SLDestroy(&s);
return 0;
}