SeqList.h
#pragma once // 防止重复包含
//---------动态顺序表------------//
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#define M 1000
// 加入typedef 可以进行简写
typedef int SLDateType; // 数据类型
typedef struct Seqlist
{
SLDateType* a;
int size; // 记录存储了多少个有效数据
int capacity; //空间容量大小
}SL;
void SLInit(SL* ps); //初始化
void SLDestory(SL* ps); //销毁
void SLPushBack(SL* ps, SLDateType x); // 尾插
void SLPrint(SL* ps); //打印函数
void SLPopBack(SL* ps); //尾删
void SLPushFront(SL* ps, SLDateType x); //头插
void SLPopFront(SL* ps); //头删
void SLCheckCapacity(SL* ps); //检查是否需要扩容
//中间插入删除
void SLInsert(SL* ps, int pos, SLDateType x); //在pos位置插入数据
void SLErase(SL* ps, int pos); //删除pos位置的数据
void SLsize(SL* ps); // 测试 size 的值
int SLFind(SL* ps, SLDateType x); //查找某一个数字的位置
int SLFinds(SL* ps, SLDateType x, int begin); // 确定查找数字的起始位置
Seqlist.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
void SLPrint(SL *ps) // 打印函数
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
// 测试 size 的值
void SLsize(SL* ps)
{
assert(ps);
printf("%d\n", ps->size);
}
void SLInit(SL *ps) // 初始化
{
assert(ps);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
//检查是否需要扩容
void SLCheckCapacity(SL* ps)
{
assert(ps);
// 扩容
if (ps->size == ps->capacity)//检查是否越界
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDateType* temp = (SLDateType*)realloc(ps->a, newCapacity * sizeof(SLDateType)); //realloc 的用法
if (temp == NULL)
{
perror("realloc fail");
exit(-1); //异常终止
}
ps->a = temp;
ps->capacity = newCapacity;
}
}
void SLDestory(SL* ps) //销毁
{
assert(ps);
if (ps->a != NULL)
{
free(ps->a);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
}
void SLPushBack(SL* ps, SLDateType x) // 尾部插入数据
{
assert(ps);
// 检查是否需要扩容
SLCheckCapacity(ps);
// 进行尾部插入
ps->a[ps->size] = x;
ps->size++;
}
void SLPopBack(SL* ps) //尾部删除数据
{
assert(ps);
//ps->a[ps->size - 1] = 0; // size 是从 0 开始的 所以要减去 1
// 温柔的方式
/*if (ps->size == 0)
{
printf("顺序表已空\n");
return;
}*/
// 暴力的方式
assert(ps->size > 0);
ps->size--;
}
// 头插 O(N) 尽量用尾插
// 需要从最后一个数据开始依次向后挪动,空出第一个位置,保证头插的位置
void SLPushFront(SL* ps, SLDateType 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++;
}
// 头删 O(1)
// 需要从前往后挪
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--;
}
//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDateType x)
{
assert(ps);
assert(pos >= 0); // pos 的位置,因该在有效位置
assert(pos <= ps->size); // 等于 “=” size 相当于尾插
SLCheckCapacity(ps); // 扩容
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
//删除pos位置的数据
// 其中 SLErase(ps,0) 就是头删
// 其中 SLErase(ps,ps-size-1) 就是尾删
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0);
assert(pos < ps->size);
assert(ps->size > 0); //检查 size 是否为空
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
// 查找某一个数据的位置是
int SLFind(SL* ps, SLDateType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
// begin 查找 x 的起始位置
int SLFinds(SL* ps, SLDateType x, int begin)
{
assert(ps);
for (int i = begin; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
// ----------- 顺序表 ----------- //
// 顺序表:顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,
// 一般情况下采用数组存储。在数组上完成数据的增删查改。
// 重点:要求连续存储 与数组的区别 :数组是随意存储
// 而顺序表 是连续的存储必须是挨着的
// 1.静态顺序表 --- 不太实用
// 对于数组开打了,浪费 。开少了,不够用
// 2.动态顺序表 --- 按需扩展空间
#include "SeqList.h"
// 头插测试
void TestSeqList3()
{
SL s1; // 创建一个结构体变量
SLInit(&s1); // 把结构体变量的地址传过去
SLPushFront(&s1, 1);
SLPushFront(&s1, 2);
SLPushFront(&s1, 3);
SLPushFront(&s1, 4);
SLPrint(&s1); // 4 3 2 1
SLDestory(&s1);
}
//头删测试
void TestSeqList4()
{
SL s1; // 创建一个结构体变量
SLInit(&s1); // 把结构体变量的地址传过去
SLPushFront(&s1, 1);
SLPushFront(&s1, 2);
SLPushFront(&s1, 3);
SLPushFront(&s1, 4);
SLPrint(&s1);
SLPopFront(&s1);
SLPopFront(&s1);
SLPopFront(&s1);
SLPopFront(&s1);
//SLPopFront(&s1);
SLPrint(&s1);
SLPushFront(&s1, 9);
SLPrint(&s1);
SLDestory(&s1);
}
// 测试 size 的值
// 注意 size 的值是从 0 开始的
void TestSeqList5()
{
SL s1; // 创建一个结构体变量
SLInit(&s1); // 把结构体变量的地址传过去
SLPushFront(&s1, 1);
SLPushFront(&s1, 2);
SLPushFront(&s1, 3);
SLPushFront(&s1, 4);
SLPrint(&s1); // 4 3 2 1
SLsize(&s1); // 4 也就是说,size 占了 5 个位置
SLDestory(&s1);
}
// 测试任意位置的插入SLInsert
void TestSeqList6()
{
SL s1; // 创建一个结构体变量
SLInit(&s1); // 把结构体变量的地址传过去
SLPushBack(&s1, 1);
SLPushBack(&s1, 2);
SLPushBack(&s1, 3);
SLPushBack(&s1, 4);
SLPrint(&s1); // 1 2 3 4
SLInsert(&s1, 2, 20);
SLPrint(&s1); // 1 2 20 3 4
SLInsert(&s1, 5, 500);
SLPrint(&s1); // 1 2 20 3 4 500 //尾插
SLInsert(&s1, 0, 400); // 400 1 2 20 3 4 500 // 头插
SLPrint(&s1);
SLDestory(&s1);
}
// 测试任意位置的删除 SLErase
void TestSeqList7()
{
SL s1; // 创建一个结构体变量
SLInit(&s1); // 把结构体变量的地址传过去
SLPushBack(&s1, 1);
SLPushBack(&s1, 2);
SLPushBack(&s1, 3);
SLPushBack(&s1, 4);
SLPrint(&s1); // 1 2 3 4
SLErase(&s1, 2);
SLPrint(&s1); // 1 2 4
SLErase(&s1, 2);
SLPrint(&s1); // 1 2
SLErase(&s1, 0);
SLPrint(&s1); // 2
SLDestory(&s1);
}
// 测试查找某一个数字,并将这个是数字删除
void TestSeqList8()
{
SL s1; // 创建一个结构体变量
SLInit(&s1); // 把结构体变量的地址传过去
SLPushBack(&s1, 1);
SLPushBack(&s1, 2);
SLPushBack(&s1, 3);
SLPushBack(&s1, 4);
SLPushBack(&s1, 5);
SLPushBack(&s1, 6);
SLPushBack(&s1, 7);
SLPushBack(&s1, 8);
SLPrint(&s1); // 1 2 3 4 5 6 7 8
int pos = SLFind(&s1, 5);
if (pos != -1)
{
SLErase(&s1, pos);
}
SLPrint(&s1); // 1 2 3 4 6 7 8
SLDestory(&s1);
}
// 测试 删除顺序表中 所有的 5
void TestSeqList9()
{
SL s1; // 创建一个结构体变量
SLInit(&s1); // 把结构体变量的地址传过去
SLPushBack(&s1, 1);
SLPushBack(&s1, 5);
SLPushBack(&s1, 3);
SLPushBack(&s1, 4);
SLPushBack(&s1, 5);
SLPushBack(&s1, 5);
SLPushBack(&s1, 7);
SLPushBack(&s1, 8);
SLPrint(&s1); // 1 5 3 4 5 5 7 8
int pos = SLFinds(&s1, 5, 0);
while (pos != -1)
{
SLErase(&s1, pos);
pos = SLFinds(&s1, 5, pos);
}
SLPrint(&s1); // 1 3 4 7 8
SLDestory(&s1);
}
// 越界是不一定报错的
// 越界 读 基本都查不出来 只能是程序的值会出现值
// 越界 写 可能会报错
void menu()
{
printf("***********************************************************\n");
printf("1、尾插数据 2、尾删数据\n");
printf("\n");
printf("3、头插数据 4、头删数据\n");
printf("\n");
printf("5、在任意位置插入数据(位置3插入20)\n");
printf("\n");
printf("6、在任意位置删除数据 \n");
printf("\n");
printf("7、查找某个数据的位置,并删除它 \n");
printf("\n");
printf("8、删除顺序表中有的 某个数据 \n");
printf("\n");
printf("9、打印数据 \n");
printf("\n");
printf("-1、退出 \n");
printf("\n");
printf("***********************************************************\n");
}
int main()
{
printf("************* 欢迎大家来到动态顺序表的测试 **************\n");
int option = 0;
SL s;
SLInit(&s);
do
{
menu();
printf("请输入你的操作:>");
scanf("%d", &option);
int sum = 0;
int x = 0;
int y = 0;
int z = 0;
int pos = 0;
int w = 0;
switch (option)
{
case 1:
printf("请依次输入你要尾插的数据:,以-1结束\n");
scanf("%d", &sum);
while (sum != -1)
{
SLPushBack(&s, sum); // 1.尾插
scanf("%d", &sum);
}
break;
case 2:
SLPopBack(&s); // 2.尾删
break;
case 3:
scanf("%d", &x);
SLPushFront(&s, x); // 3.头插
break;
case 4:
SLPopFront(&s); // 4.头删
break;
case 5:
SLInsert(&s, 3, 20); // 5.在任意位置插入数据
break;
case 6:
SLErase(&s, 3); // 6.在任意位置删除数据
break;
case 7:
printf("请输入要删除序列的中的某个数字\n");
scanf("%d", &z);
y = SLFind(&s, z); // 7.查找某个数字的位置,并且删除它
printf("%d的位置在%d处: \n", z, y);
if (y != -1)
{
SLErase(&s, y);
}
break;
case 8:
printf("请输入要删除序列的中的数字\n"); //8.删除顺序表中 所有的 某个数据
scanf("%d", &w);
pos = SLFinds(&s, w, 0);
while (pos != -1)
{
SLErase(&s, pos);
pos = SLFinds(&s, w, pos);
}
break;
case 9:
SLPrint(&s);
break;
default:
if (option == -1)
{
exit(0); // 退出程序
}
else
{
printf("输入错误,请重新输入\n");
}
break;
}
} while (option != -1); // 退出程序
SLDestory(&s);
return 0;
}