⭐前言⭐
※※※大家好!我是同学〖森〗,一名计算机爱好者,今天让我们进入学习模式。若有错误,请多多指教。更多有趣的代码请移步Gitee
👍 点赞 ⭐ 收藏 📝留言 都是我创作的最大的动力!
目录
※※※大家好!我是同学〖森〗,一名计算机爱好者,今天让我们进入学习模式。若有错误,请多多指教。更多有趣的代码请移步Gitee
(2)在XXX工程项目建XXX源程序文件(.cpp)和XXX头文件(.h)
(6)在顺序表中第 i 个位置之前插入一个新元素 NewElem。
(11)删除第DelPos个元素,并用DelElement返回其值
(13) 将线性表中第Pos个的元素值修改成参数Elem的值。
6、 在顺序表中第 i 个位置之前插入一个新元素 NewElem
11、删除第DelPos个元素,并用DelElement返回其值
9、找CurElem 的前驱,结果由 PriorElem 输出
10、找 CurElem 的后继,结果由 NextElem 输出
1.顺序表
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。
1、线性表顺序存储的物理结构(如图所示)① Elem:存储存放表元素连续内存空间的首地址;② Length:存储 Elem 所指空间中存放的元素个数;③ ListSize:存储 Elem 空间的大小,该大小值以元素个数为单位来体现。④ 注意,在线性表的逻辑结构中,元素在线性表中的位置次序是从 1 开始计数的。⑤ 线性表 L 实质是一个具有三个域的结构类型的变量。
2.实现顺序表的功能
//将光标移动到指定位置 ,这个是我测试时用的,和顺序表无关
void GoToxy(short x, short y);
// 构造一个空的顺序表
void InitList(SqList_p L);
// 销毁顺序表,即释放Elem对应的内存空间。
void DestroyList(SqList_p L);
// 将顺序表重置为空表。
void ClearList(SqList_p L);
// 判断顺序表是否为空表。
Status ListEmpty(SqList L);
// 返回顺序表中元素的个数。
unsigned long ListLength(SqList L);
// 在顺序表中第 i 个位置之前插入一个新元素 NewElem。
Status ListInsert(SqList_p L, unsigned long i, ListElemType NewElem);
// 用Elem返回顺序表中第i个数据元素的值
Status ListGetElem(SqList L, unsigned long i, ListElemType& Elem);
// 返回顺序表中第一个与Elem满足关系,不存在返回0
unsigned long ListLocateElem(SqList L, ListElemType Elem);
// 找CurElem 的前驱,结果由 PriorElem 输出。
Status ListGetPriorElem(SqList L, ListElemType CurElem, ListElemType& PriorElem);
// 找 CurElem 的后继,结果由 NextElem 输出。
Status ListGetNextElem(SqList L, ListElemType CurElem, ListElemType& NextElem);
//删除第DelPos个元素,并用DelElement返回其值
Status ListDelete(SqList_p L, unsigned long DelPos, ListElemType& DelElem);
// 正向遍历顺序表
void ListTraverse(SqList L);
// 逆向遍历顺序表
void ListReverseTraverse(SqList L);
// 将线性表中第Pos个的元素值修改成参数Elem的值。
Status ListModifyElem(SqList L, unsigned long Pos, ListElemType Elem);
3.准备工作
编译器:VS2019
以下代码在SqList.h中实现
(1)创建一个XXX工程项目
(2)在XXX工程项目建XXX源程序文件(.cpp)和XXX头文件(.h)
我这里创建了SqList.h (用来声明函数,定义结构体)、 Main.cpp(测试顺序表的函数功能) 和 MySqList.cpp(实现顺序表的功能)
(3)准备头文件
#include <stdlib.h>
#include <malloc.h>
#include <stdio.h>
#include<windows.h>
一般将系统头文件用< >括起来,自定义头文件用" "括起来。
(4) 定义一些宏
#define OVERFLOW -2
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define LIST_INIT_SIZE 50 //线性表存储空间的初始分配量
#define LIST_INCREMENT 50 //线性表存储空间的分配增量
typedef int Status; // 将int类型重新命名为ElemType。
typedef int ListElemType; //将int类型重新命名为ElemType,即ElemType可以代替int
(5)顺序表数据类型的定义。
typedef struct
{
ListElemType* Elem; //存储空间基址
unsigned long Length; // 线性表长度
unsigned long ListSize; //当前分配的存储容量(以sizeof(ElemType)为单位)
} SqList,*SqList_p;
(6)使用枚举来替换switch中的选项
enum Option
{
MyInitList = 1, //初始化 1
MyDestroyList, //销毁空间 2
MyClearList, //清空列表 3
MyListEmpty, //判断为空 4
MyListLength, //获取长度 5
MyListInsert, //位置插入 6
MyListGetElem, //位置找查 7
MyListLocateElem, //内容找查 8
MyListGetPriorElem, //获取前驱 9
MyListGetNextElem, //获取后继 10
MyListDelete, //删除 11
MyListTraverse, //顺序打印 12
MyListReverseTraverse, //逆序打印 13
MyListModifyElem, //修改顺序表元素 14
};
4.功能实现
这部分代码在MySqList.cpp中实现
(1) 初始化线性表:构造一个空的顺序线性表
void InitList(SqList_p L) {
L->Elem = (ListElemType*)malloc(LIST_INIT_SIZE * sizeof(ListElemType)); //动态申请内存
if (L->Elem == NULL) exit(OVERFLOW); //存储分配失败
L->Length = 0; // 设置线性表中无元素,即空表。
L->ListSize = LIST_INIT_SIZE; // 记录 Elem 所对应空间的大小。
return;
}
a. 这里的ListElemType*和ListElemType分别代表int*和int,通过修改typedef int ListElemType中的int;可以将顺序表换非其他类型。
b. malloc的使用:指针自身 = (指针类型*)malloc(sizeof(指针类型)*数据数量)具体内容请移步-->malloc的使用
编码中的意思是,开辟50个int类型大小的空间。
c. LIST_INIT_SIZE: 这里表示50,修改 #define LIST_INIT_SIZE 50 的50,可以重新定义开辟空间的大小。
(2)销毁顺序表
void DestroyList(SqList_p L) {
free(L->Elem); // 释放Elem所对应的内存空间。
L->Elem = NULL; //将储存空间置NULL
L->Length = L->ListSize = 0; // 设置两者为0,使得数据的物理存储值与语义一致。
return;
}
(3)将顺序表重置为空表
void ClearList(SqList_p L) {
free(L->Elem); //释放Elem所对应的内存空间。
InitList(L); //设置空表
return;
}
(4)判断顺序表是否为空表
Status ListEmpty(SqList L) {
// Length 的值为 0 表示线性表为空,即表中无元素。
if (L.Length == 0){
return TRUE;
}else {
return FALSE;
}
}
a. Status:这里表示的是int类型
b. TRUE和FALSE:这里是定义的宏变量
#define TRUE 1
#define FALSE 0
(5)返回顺序表中元素的个数
unsigned long ListLength(SqList L) {
return L.Length;
}
这里的返回类型设置为无符号长整形 unsigned long 是因为顺序表中的元素个数不可能是负数。
(6)在顺序表中第 i 个位置之前插入一个新元素 NewElem。
//检查空间容量
void CheckCap(SqList_p L) {
//增容
if (L->Length == L->ListSize) {
ListElemType* newbase = (ListElemType*)realloc(L->Elem, (L->ListSize + LIST_INCREMENT) * sizeof(ListElemType));
if (NULL == newbase) {
exit(OVERFLOW);
}else {
L->Elem = newbase;
}
L->ListSize += LIST_INCREMENT;
}
return ;
}
// 在线性表中第 i 个元素之前插入一个新元素 NewElem。,线性表中元素的顺序从 1 开始计数
Status ListInsert(SqList_p L, unsigned long i, ListElemType NewElem) {
if (i < 1 || i > L->Length + 1) {
return FALSE;
}
CheckCap(L); //检查Elem容量
for (long j = (L->Length - 1); j >= (long)(i - 1); j--) {
L->Elem[j + 1] = L->Elem[j];
}
L->Elem[i - 1] = NewElem;
L->Length++;
return OK;
}
a. 新开辟的空间不要直接给L->Elem ,而是使用newbase判断下是否开辟空间失败,以防已有数据丢失。
b. realloc:指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。详细内容请移步-->realloc
c. i的插入范围是1<= i <=L->Length,所以类型使用为无符号长整形,i为其他值则返回FALSE
d. 将i个元素一同向后移动一位 注意:在判断j>=(long)(i - 1)时,不要写成 j >= i -1;
因为long类型数据与unsigned long 类型的数据比较会把long类型的数据转换为unsigned long后在进行比较,这时如果顺序表为空的话就会出现bug,-1转换为无符号长整形会非常大。如图所示:
(7)用Elem返回顺序表中第i个数据元素的值
Status ListGetElem(SqList L, unsigned long i, ListElemType& Elem)
{
if (i < 1 || i > L.Length) return ERROR;
Elem = L.Elem[i - 1]; // Elem 数组中下标为 i-1 的元素即为线性表中的第 i 个元素。
return OK;
(8)返回顺序表中第一个与Elem满足关系,不存在返回0
unsigned long ListLocateElem(SqList L, ListElemType Elem)
{
if (0 == L.Length) return FALSE;
for (long i = 0; i < L.Length; i++) {
if (Elem == L.Elem[i]) {
return i + 1; //找到了,返回顺序表的位置,不是Elem数组的位置。
}
}
return FALSE;
}
(9)找CurElem的前驱元素
// 在线性表中找出参数 CurElem 值所代表元素的直接前驱元素,结果由参数 PriorElem 输出。
Status ListGetPriorElem(SqList L, ListElemType CurElem, ListElemType& PriorElem) {
unsigned long CurElemPos = ListLocateElem(L, CurElem);
// 在线性表中进行查找。返回CurElem的位置
if (CurElemPos == 0 || CurElemPos == 1)
// CurElemPos 为 0 说明表中无 CurElem 元素;
// CurElemPos 为 1 说明 CurElem 是表中第 1 个元素,无前驱。
return ERROR;
else
{
PriorElem = L.Elem[CurElemPos - 2];
return OK;
}
}
(10)找CurElem的后继元素
// 在线性表中找出 CurElem 参数所代表元素的直接后继元素,结果由参数 NextElem 输出。
Status ListGetNextElem(SqList L, ListElemType CurElem, ListElemType& NextElem) {
unsigned long CurElemPos = ListLocateElem(L, CurElem);
if (CurElemPos == 0 || CurElemPos == L.Length)
return ERROR; // CurElemPos 等于 Length,说明 CurElem 是表中最后一个元素,无后继。
else {
NextElem = L.Elem[CurElemPos];
return OK;
}
}
(11)删除第DelPos个元素,并用DelElement返回其值
//删除第DelPos个元素,并用DelElement返回其值
Status ListDelete(SqList_p L, unsigned long DelPos, ListElemType& DelElem) {
//i的合法值为1≤i≤ListLength_Sq(L)
if (DelPos<1 || DelPos>L->Length) return ERROR; //DelPos值不合法
if (0 == L->Length) return ERROR; //空表无法进行删除
DelElem = L->Elem[DelPos - 1];
for (long i = DelPos - 1; i < L->Length; i++) {
L->Elem[i] = L->Elem[i + 1]; //被删除元素之后的元素左移
}
L->Length--; //表长减1
return OK;
}
(12)正向遍历顺序表和逆向变量顺序表
// 正向遍历线性表,即从线性表中的第 1 个元素开始顺序地向后依次输出表中的全部元素值。
void ListTraverse(SqList L){
printf("[");
for (long i = 0; i < L.Length; i++) {
printf("%d ", L.Elem[i]); // 此处,表元素为整型数据。可根据需要进行修改。
}
printf("]\n");
return;
}
// 逆向遍历线性表,即从线性表中最后的元素开始顺序地向前依次输出表中的全部元素值。
void ListReverseTraverse(SqList L){
printf("[");
for ( long i = L.Length -1; i >= 0; i--) {
printf("%d ", L.Elem[i]); // 此处,表元素为整型数据。可根据需要进行修改。
}
printf("]\n");
return;
}
(13) 将线性表中第Pos个的元素值修改成参数Elem的值。
// 将线性表中第Pos个的元素值修改成参数Elem的值。
Status ListModifyElem(SqList L, unsigned long Pos, ListElemType Elem)
{
if (Pos < 1 || Pos > L.Length) return ERROR;
L.Elem[Pos - 1] = Elem;
return OK;
}
(14)将光标移动到(x,y)处
//将光标移动到指定位置
void GoToxy(short x, short y) {
COORD coord = { x, y };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
5.测试功能
这部分代码在Main.cpp中实现
#include"SqList.h"
void menu()
{
printf("\n#################################################################################\n");
printf("\n▓ * SqList测试系统 * ▓\n");
printf("\n#################################################################################\n");
printf("\n ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆\n");
printf(" |◆|******|◆| ☆ 1 初始化 ☆ 2 销毁空间 |◆|******|◆|\n");
printf(" |◆|******|◆| ☆ 3 清空列表 ☆ 4 判断为空 |◆|******|◆|\n");
printf(" |◆|******|◆| ☆ 5 获取长度 ☆ 6 位置插入 |◆|******|◆|\n");
printf(" |◆|******|◆| ☆ 7 位置找查 ☆ 8 内容找查 |◆|******|◆|\n");
printf(" |◆|******|◆| ☆ 9 获取前驱 ☆10 获取后继 |◆|******|◆|\n");
printf(" |◆|******|◆| ☆11 删 除 ☆12 顺序打印 |◆|******|◆|\n");
printf(" |◆|******|◆| ☆13 逆序打印 ☆14 修改元素 |◆|******|◆|\n");
printf(" |◆|******|◆| |◆|******|◆|\n");
printf(" ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆\n");
printf("\n#################################################################################\n");
printf("\n▓ *** 请选择<1~14>: *** ▓\n");
printf("\n#################################################################################\n");
}
int main()
{
#pragma warning (disable:4996) //消除警告
system("color 2"); //改变颜色
int input = -1; //选择控制变量
SqList list = { NULL,0,0 };
SqList_p sqlist = &list; //我的顺序表
InitList(sqlist); //初始化
ListInsert(sqlist, 1, 1);
ListInsert(sqlist, 2, 2);
ListInsert(sqlist, 3, 3);
ListInsert(sqlist, 4, 4);
ListInsert(sqlist, 5, 5); //测试数据
while (TRUE) //多次选择
{
menu(); //打印菜单
GoToxy(50, 20); //移动光标到(50,20)处
rewind(stdin);//清除缓存区多余的内容
scanf("%d", &input);
int j = 0;
GoToxy(0, 24); //移动光标到(0,24)处
switch (input)
{
//使用枚举变量,使单词代替数字。
case MyInitList:
printf("开始初始化线性表\n");
InitList(sqlist);
printf("初始化线性表成功\n");
system("pause"); //按任意键继续
break;
case MyDestroyList:
printf("开始销毁线性表\n");
DestroyList(sqlist);
printf("销毁线性表成功\n");
system("pause");
break;
case MyClearList:
printf("开始清空线性表\n");
ClearList(sqlist);
printf("清空线性表成功\n");
system("pause");
break;
case MyListEmpty:
printf("开始判断线性表是否为空\n");
j = ListEmpty(*sqlist);
if (j == TRUE) {
printf("线性表为空\n");
} else{
printf("线性表不为为空\n");
}
system("pause");
break;
case MyListLength:
printf("开始线性表的长度\n");
unsigned long length;
length = ListLength(*sqlist);
printf("线性表的长度为:%ld\n", length);
system("pause");
break;
case MyListInsert:
unsigned long insertpos;
ListElemType newelem;
printf("请输入插入元素和插入位置用,隔开:");
scanf("%d,%d", &newelem, &insertpos);
j = ListInsert(sqlist, insertpos, newelem);
if (j == OK) {
printf("插入成功\n");
}
else {
printf("插入失败\n");
}
system("pause");
break;
case MyListGetElem:
unsigned long getpos;
ListElemType getelem;
printf("请输入线性表的位置:");
scanf("%d", &getpos);
j = ListGetElem(*sqlist, getpos, getelem);//清空通讯录
if (j == OK) {
printf("%d位置的元素是%d\n", getpos, getelem);
}
else {
printf("查询失败!\n");
}
system("pause");
break;
case MyListLocateElem:
ListElemType locateelem;
printf("请输入需要查询的元素:");
scanf("%d", &locateelem);
j = ListLocateElem(*sqlist, locateelem);
if (j == FALSE) {
printf("该元素不存在\n");
}
else {
printf("%d元素的位置为%d\n", locateelem, j);
}
system("pause");
break;
case MyListGetPriorElem:
ListElemType curelem, priorelem;
printf("请输入要找查的元素:");
scanf("%d", &curelem);
j = ListGetPriorElem(*sqlist, curelem, priorelem);
if (j == 0) {
printf("该元素不存在或没有前驱\n");
}
else {
printf("%d的前面是%d\n", curelem, priorelem);
}
system("pause");
break;
case MyListGetNextElem:
ListElemType nextelem;
printf("请输入要找查的元素:");
scanf("%d", &curelem);
j = ListGetNextElem(*sqlist, curelem, nextelem);
if (j == 0 || j == sqlist->Length) {
printf("该元素不存在或没有后继\n");
}
else {
printf("%d的后面是%d\n", curelem, nextelem);
}
system("pause");
break;
case MyListDelete:
ListElemType delelem;
unsigned long delpos;
printf("请输入删除位置:");
scanf("%ld", &delpos);
j = ListDelete(sqlist, delpos, delelem);
if (ERROR == j) {
printf("删除失败!\n");
}
else {
printf("删除成功,删除的是%d\n", delelem);
}
system("pause");
break;
case MyListTraverse:
printf("开始顺序打印!\n");
ListTraverse(*sqlist);
printf("打印完成!\n");
system("pause");
break;
case MyListReverseTraverse:
printf("开始逆序打印!\n");
ListReverseTraverse(*sqlist);
printf("打印完成!\n");
system("pause");
break;
case MyListModifyElem:
ListElemType elem;
unsigned long pos;
printf("请输入修改位置和修改的数据(用逗号隔开):");
scanf("%ld,%d", &pos, &elem);
j = ListModifyElem(*sqlist, pos, elem);
if (ERROR == j) {
printf("修改失败!\n");
}
else {
printf("修改成功\n");
printf("%ld位置元素改为:%d\n", pos, elem);
}
system("pause");
break;
case 0:
printf("退出测试!");
return 0;
default:
printf("输入错误!!!");
system("pause");
break;
}
system("cls");//清屏
}
return 0;
}
6.运行结果演示:
为了方便演示,在这里线性表一次赋值为1,2,3,4,5
1、初始化顺序表
2、销毁空间
3、清空列表
4、判断顺序表是否为空
5、返回顺序表中元素的个数
6、 在顺序表中第 i 个位置之前插入一个新元素 NewElem
在3号位置插入99
12、正向遍历顺序表
11、删除第DelPos个元素,并用DelElement返回其值
7、用Elem返回顺序表中第i个数据元素的值
8、返回顺序表中第一个与Elem满足关系,不存在返回0
9、找CurElem 的前驱,结果由 PriorElem 输出
10、找 CurElem 的后继,结果由 NextElem 输出
13、逆向遍历顺序表
14、将线性表中第Pos个的元素值修改成参数Elem的值
7.注意事项
1、引用类型可以将形参变量带回主函数,但是这是C++支持的,C语言时不支持的。DelElem就是引用类型。
例:
Status ListDelete(SqList_p L, unsigned long DelPos, ListElemType& DelElem)
2、 . 和 -> 的区别:->前面是指向结构体的指针变量,而.前面需要时结构体变量
3、顺序表是从1开始的,而数组是从0下标开始的。
4、unsigned long 和 long 类型的数据比较时,会先把 long类型的数据转换成unsigned long类型后进行比较,负数就会变得很大。
8.完整代码
完整的代码和更多有趣的代码;请移步到小森的Gitee仓库 -->Gitee