一、概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数据存储。在数组上完成数据的增删查改。
顺序表的数据必须从第一个位置开始,连续存储的。
顺序表一般可以分为:
1.1 静态顺序表:使用定长数组存储元素。
1.2 动态顺序表:使用动态开辟的数组元素。
1.3 接口实现
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
typedef int SqLDataType;
// 动态顺序表
typedef struct SeqList
{
SqLDataType* arr; // 指向动态数组指针
int size; // 有效数据个数
int capicity; // 容量空间大小
}Sqlist;
//基本增删查改接口
//顺序表初始化
void SeqListInit(SeqList* ps1);
//检查空间,如果满了,进行增容
void checkcapacity(SeqList* ps1);
//顺序表尾插
void seqListPushBack (SeqList* ps1,SLDataType x);
//顺序表尾删
void seqListPopBack(seqList* ps1);
//顺序表头插
void seqListPushFront(SeqList* ps1,SLDataType x);
//顺序表头删
void seqListPopFront(SeqList* ps1);
//顺序表查找
int seqListFind(seqList* ps1,SLDataType x);
//顺序表在pos位置插入×
void SeqListInsert(SeqList* ps1, size_t pos,SLDataType x);
//顺序表删除pos位置的值
void seqListErase(seqList* ps1,size_t pos);
//顺序表销毁
void seqListDestory(seqList* ps1);
//顺序表打印
void seqListPrint(SeqList* ps1);
一般简单的程序就写一个源文件,再在里面写几个函数或者在 main 函数里面写就搞定了。但是相对复杂一点的我们分开来写,下面开始来写。
我们把顺序表的声明放到 SqList.h 文件里面;顺序表的定义放到 SqList.c 文件里面;测试顺序表放到 Test.c 文件里面。
SqList.h 文件
#pragma once // 防止头文件被重复包含
#include // 头文件写在 .h 文件里面,其他两个 .c 文件包含 .h 文件就好了
#include
//#define N 20
//typedef int SqLDataType;
//
静态顺序表 —— N太小,可能不够用;N太大,可能浪费空间
//struct SeqList
//{
// SqLDataType arr[N];
// int size;
//};
typedef int SqLDataType;
// 动态顺序表
typedef struct SeqList
{
SqLDataType* arr; // 指向动态数组指针
int size; // 有效数据个数
int capicity; // 容量空间大小
}Sqlist;
// 顺序表初始化
//void SeqListInit(Sqlist Sql);
void SeqListInit(Sqlist* Sql);
// 增删查改
SqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SqList.h"
//void SeqListInit(Sqlist Sql)
//{
// // 初始化可以直接 malloc 开辟空间,也可以先初始化为空
// Sql.arr = NULL;
// Sql.size = Sql.capicity = 0;
//}
void SeqListInit(Sqlist* Psql)
{
// 初始化可以直接 malloc 开辟空间,也可以先初始化为空
Psql->arr = NULL;
Psql->size = Psql->capicity = 0;
}
Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SqList.h"
//void SLTest1()
//{
// Sqlist S1;
//
// // 这里其实是错误的,实参传给形参,形参是实参的一份临时拷贝,改变形参不影响实参,所以这里需要把传值调用改变为传址调用
// SeqListInit(S1);
//}
void SLTest2()
{
Sqlist S1;
SeqListInit(&S1);
}
int main()
{
// 建议每写一个模块功能然后测试一下写的那个模块;如果全部功能一下子都写完了,没错误还好,但是如果错误比较偏多,自己也不好去调试找出来
//SLTest1();
SLTest2();
return 0;
}
上面代码只实现初始化部分,第三段 Test.c 文件里面注释部分加以注释说:”建议每写一个模块功能然后测试一下写的那个模块;如果全部功能一下子都写完了,没错误还好,但是如果错误比较偏多,自己也不好去调试找出来”。这种方法可以是初学者少走很多弯路,减少因代码错误而查找代码错误的时间。
下面是所有全部代码,并加以注释:
// SqList.h 文件
//
#pragma once // 防止头文件被重复包含
#include
#include
#include
// 动态
typedef int SqlDataType;
typedef struct SqList
{
SqlDataType* data; // 指向动态数组指针
int size; // 有效数据个数
int Max_capacity; // 最大容量
}Sqlist;
// 初始化 Sqlist
void SqlistInit(Sqlist* Psql);
// 检查容量是否满了
void SqlistCheckCapacity(Sqlist* Psql);
// 增删查改
// 头插
void SqlistPushFront(Sqlist* Psql, SqlDataType data);
// 尾插
void SqlistPushBack(Sqlist* Psql, SqlDataType data);
// 头删
void SqlistPopFront(Sqlist* Psql);
// 尾删
void SqlistPopBack(Sqlist* Psql);
// 在某个位置插入数据
void SqlistInsert(Sqlist* Psql, int pos, SqlDataType data);
// 在某个位置删除数据
void SqlistErase(Sqlist* Psql, int pos);
// 查询某个数据
int SqlistFind(Sqlist* Psql, SqlDataType data);
// 修改某个位置的数据
void SqlistModify(Sqlist* Psql, int pos, SqlDataType data);
// 释放内存
void SqlistDestory(Sqlist* Psql);
// 打印
void SqlistPrint(Sqlist* Psql);
// SqList.c 文件
//
#include "SqList.h"
// 初始化
void SqlistInit(Sqlist* Psql)
{
assert(Psql);
Psql->data = NULL;
Psql->size = 0;
Psql->Max_capacity = 0;
}
// 检查容量是否满了
void SqlistCheckCapacity(Sqlist* Psql)
{
assert(Psql);
if(Psql->size == Psql->Max_capacity)
{
int New_Max_capacity = 0;
New_Max_capacity = Psql->Max_capacity == 0 ? 3 : Psql->Max_capacity * 2;
SqlDataType* ret = (SqlDataType*)realloc(Psql->data, New_Max_capacity * sizeof(SqlDataType));
if(ret == NULL)
{
perror("SqlistCheckCapacity:realloc:");
exit(-1);
}
else
{
Psql->data = ret;
Psql->Max_capacity = New_Max_capacity;
}
}
}
// 头插
void SqlistPushFront(Sqlist* Psql, SqlDataType data)
{
// assert(Psql);
// SqlistCheckCapacity(Psql);
// int end = Psql->size - 1;
// while (end >= 0)
// {
// Psql->data[end + 1] = Psql->data[end];
// end--;
// }
// Psql->data[0] = data;
// Psql->size++;
SqlistInsert(Psql, 0, data);
}
// 尾插
void SqlistPushBack(Sqlist* Psql, SqlDataType data)
{
// assert(Psql);
// SqlistCheckCapacity(Psql);
// Psql->data[Psql->size] = data;
// Psql->size++;
SqlistInsert(Psql, Psql->size, data);
}
// 头删
void SqlistPopFront(Sqlist* Psql)
{
// assert(Psql);
// assert(Psql->size > 0);
// int begin = 0;
// while(begin < Psql->size-1)
// {
// Psql->data[begin] = Psql->data[begin+1];
// begin++;
// }
// Psql->size--;
SqlistErase(Psql, 0);
}
// 尾删
void SqlistPopBack(Sqlist* Psql)
{
// assert(Psql);
// // 温柔检查
// // if(Psql->size == 0)
// // {
// // printf("没有数据可删!\n");
// // return;
// // }
// // else
// // {
// // Psql->size--;
// // }
// // 暴力检查 直接报错 显示哪里错误
// assert(Psql->size > 0);
// Psql->size--;
SqlistErase(Psql, Psql->size);
}
// 在某个位置插入数据
void SqlistInsert(Sqlist* Psql, int pos, SqlDataType data)
{
assert(Psql);
assert(pos >= 0 && pos <= Psql->size);
SqlistCheckCapacity(Psql);
int end = Psql->size-1;
while(pos <= end)
{
Psql->data[end+1] = Psql->data[end];
end--;
}
Psql->data[pos] = data;
Psql->size++;
}
// 在某个位置删除数据
void SqlistErase(Sqlist* Psql, int pos)
{
assert(Psql);
assert(pos >= 0 && pos < Psql->size);
int begin = pos;
while(begin < Psql->size-1)
{
Psql->data[begin] = Psql->data[begin+1];
begin++;
}
Psql->size--;
}
// 查询某个数据
int SqlistFind(Sqlist* Psql, SqlDataType data)
{
assert(Psql);
for(int i = 0; i < Psql->size; i++)
{
if(data == Psql->data[i])
{
return i;
}
}
printf("查不到此位置!\n");
return -1;
}
// 修改某个位置的数据
void SqlistModify(Sqlist* Psql, int pos, SqlDataType data)
{
assert(Psql);
assert(pos >=0 && pos < Psql->size);
Psql->data[pos] = data;
}
// 释放内存
void SqlistDestory(Sqlist* Psql)
{
if(Psql->data)
{
free(Psql);
Psql->data = NULL;
Psql->size = 0;
Psql->Max_capacity = 0;
}
}
// 打印
void SqlistPrint(Sqlist* Psql)
{
assert(Psql);
for(int i = 0; i < Psql->size; i++)
{
printf("%d ", Psql->data[i]);
}
printf("\n");
}
// Test.c 文件
//
#define _CRT_SECURE_NO_WARNINGS 1
// 顺序表就是数组
// 数据必须从第一个位置开始,连续存储的
// 数据结构帮助我们在内存中管理数据 —— 增、删、查、改
#include "SqList.h"
menu()
{
printf("*******************************************\n");
printf("***** 1. SqlistPushFront(头插) ******\n");
printf("***** 2. SqlistPushBack (尾插) ******\n");
printf("***** 3. SqlistPopFront (头删) ******\n");
printf("***** 4. SqlistPopBack (尾删) ******\n");
printf("***** 5. SqlistInsert(某个位置插) ******\n");
printf("***** 6. SqlistErase (某个位置删) ******\n");
printf("***** 7. SqlistFind (查询) ******\n");
printf("***** 8. SqlistModify(修改) ******\n");
printf("***** 9. SqlistPrint (打印) ******\n");
printf("***** 0. exit(退出并释放内存) ******\n");
printf("*******************************************\n");
}
SqlistTest()
{
Sqlist S1;
SqlistInit(&S1);
SqlDataType data = 0;
int OldData = 0;
int NewData = 0;
int ret = 0;
int pos = 0;
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("头插请输入数据:>");
scanf("%d", &data);
while (data != -1)
{
SqlistPushFront(&S1, data);
scanf("%d", &data);
}
break;
case 2:
printf("尾插请输入数据:>");
scanf("%d", &data);
while (data != -1)
{
SqlistPushBack(&S1, data);
scanf("%d", &data);
}
break;
case 3:
SqlistPopFront(&S1);
printf("头删成功!\n");
break;
case 4:
SqlistPopBack(&S1);
printf("尾删成功!\n");
break;
case 5:
printf("请分别输入要插入的位置和插入的数据:>");
scanf("%d%d", &pos, &data);
SqlistInsert(&S1, pos, data);
printf("插入数据成功!\n");
break;
case 6:
printf("请输入你要删除的数据:>");
scanf("%d", &data);
ret = SqlistFind(&S1, data);
if (ret != -1)
{
while (ret != -1)
{
SqlistErase(&S1, ret);
ret = SqlistFind(&S1, data);
}
printf("删除数据成功!\n");
}
else
{
printf("删除数据不存在!\n");
}
break;
case 7:
printf("请输入要查询的数据:>");
scanf("%d", &data);
ret = SqlistFind(&S1, data);
if (ret >= 0)
{
printf("找到了,下标是:> %d\n", ret);
}
else
{
printf("没找到查询的数据!\n");
}
break;
case 8:
SqlistPrint(&S1);
printf("请输入 OldData 和 NewData 的数据:>");
scanf("%d%d", &OldData, &NewData);
ret = SqlistFind(&S1, OldData);
if (ret != -1)
{
SqlistModify(&S1, ret, NewData);
printf("修改成功!\n");
}
else
{
printf("修改的数据不存在!\n");
}
break;
case 9:
SqlistPrint(&S1);
break;
case 0:
SqlistDestory(&S1);
printf("退出并释放内存!\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
}
int main()
{
SqlistTest();
return 0;
}
三个文件我都放在了一个代码段里面,最后的 Test.c 测试程序的文件我直接改了一个增删查改的界面。但是,建议初学者开始写的时候用我写初始化部分 Test.c 代码段里面的方法,去测试自己写的每个功能,每写一个功能去测试一下,这样可以减少查错时间,提高效率。