目录
一、实验目的及要求
1、实验目的
(1)熟悉线性表的顺序存储结构和链式存储结构,并理解其特点和适用场景。
(2)掌握循环队列的基本原理,明白队列的相关操作。
2、实验要求
敢死队任务执行模拟程序:
有n个敢死队员要炸掉敌人的一碉堡,大家很勇敢谁都想去,排长决定用轮回数数的办法来决定哪个战士去执行任务。如果前一个战士没完成任务,则要再派一个战士上去。现给每个战士编一个号,大家围坐成一圈,随便从某一个战士开始计数,当数到5时,对应的战士就去执行任务,随后此战士不再参加下一轮计数。如果此战士没完成任务,再从下一个战士开始数,被数到第5时,该战士接着去执行任务。以此类推,直到任务完成为止。
为保护排长,排长最后一个去,数到排长时自动后延到下一个战士。假设排长为1号,请你设计一程序,求出敢死队员的任务顺序。
实验要求如下:
(1)用线性表的顺序存储结构和链式存储结构分别实现任务顺序定义功能。
(2)用循环队列是否可以实现上述功能?
(3)分析并计算时间复杂度和空间复杂度。
二、功能设计
- 通过主菜单选择存储结构
- 构建敢死队的顺序表和链表
- 删除敢死队员
- 模拟执行任务顺序
三、系统流程图
四、数据结构及函数功能模块设计:
1、关于顺序表
(1)定义敢死队队员结构体Soldier_sq,包含int number来表示队员编号。
(2)定义顺序表SqList,包含Soldier_sq soldiers[MAX_SOLDIERS]数组来存储敢死队队员,还有int sum代表顺序表大小,也就是敢死队队员人数。
(3)初始化敢死队队员顺序表
void initSoldiers_sq(SqList *L, int n)
参数:SqList *L,传入一个敢死队队员顺序表L;int n,传入输入的敢死队队员人数。
功能:将敢死队队员顺序表L的大小设置为n,并按顺序为数组中的敢死队员编号。
(4)删除敢死队员
void deleteSoldier_sq(SqList *L, int i)
参数:SqList *L,传入顺序表L,表示要在L中进行删除;int i,代表要删除的队员在数组中的下标为i。
功能:先判断传入的i是否小于0或大于等于顺序表的大小sum,如果是则提示删除失败并退出,否则将数组中i后面的元素依次往前移,覆盖掉前面的元素,从而完成删除操作,最后将顺序表大小sum减1。
(5)模拟执行任务顺序
void executeTasks_sq(SqList *L, int n, int start)
参数:SqList *L,传入顺序表L,表示要在L中进行模拟执行任务排序;int n,传入敢死队队员人数;int start,传入输入的要从哪一个编号的敢死队员开始数。
功能:首先,定义了三个变量count、index和i,并初始化 count为0,index 为start - 1。然后先求出第一个战士的编号并输出与删除,然后再循环求剩下的战士的执行编号顺序并不断输出与删除。
(6)选择顺序表
void sqlistchoice()
功能:在菜单中选择顺序表存储时需要进行的相关操作与调用相关的函数,包括输入敢死队员数量,并判断输入是否有误,如果输入为1则直接输出"第1号战士排长执行任务"并返回主菜单,然后输入从哪个编号的战士开始数,以及调用initSoldiers_sq(&L, n)和executeTasks_sq(&L, n, start)函数进行相关操作。
2、关于链表
(1)定义敢死队员结构体,包含int number来表示队员编号以及Node *next指针指向下一个队员。
(2)初始化敢死队员链表
Soldier *initSoldiers(Soldier *head, int n)
参数:Soldier *head,代表链表的头结点,int n代表敢死队员人数。
功能:创建与初始化循环链表,并返回链表的基址。
(3)模拟执行任务顺序
void executeTasks(Soldier *head, int n, int start)
参数:Soldier *head,传入链表头结点也就是链表的基址;int n,传入敢死队员总人数;int start传入从哪个编号的战士开始数的编号。
功能:先求出第一个战士的编号并输出与删除,然后再循环求剩下的战士的执行编号顺序并不断输出与删除。
(4)选择链表
void linklistchoice()
功能:在菜单中选择链表存储时需要进行的相关操作与调用相关的函数,包括输入敢死队员数量,并判断输入是否有误,如果输入为1则直接输出"第1号战士排长执行任务"并返回主菜单,然后输入从哪个编号的战士开始数,以及调用head = initSoldiers(L, n)和executeTasks(head, n, start)函数进行相关操作。
3、主函数
int main()
功能:主菜单,选择选项,判断输入的是否有误,不同选项调用不同函数。
五、核心算法设计与实现
1、顺序存储结构:
(1)初始化敢死队员顺序表:
通过循环将每个队员的编号存储在顺序表中。
(2)删除敢死队员:
根据给定的索引,将要删除的队员后面的所有队员向前移动一个位置,从而删除指定位置的队员。
(3)执行任务:
核心是通过index = (index + 1) % L->sum来不断数,从指定的队员开始数,数到第五个队员,如果此队员不是排长,则输出其执行任务,然后将其删除,记录已执行任务人数的计数器count加1。
接着进入循环, 直到count达到总人数n才退出。在循环中,先数四个人,然后如果第五个人如果是排长且此时剩余的敢死队员除了排长还有其他人,则再往后数一个输出执行任务,并将其删除,count加1。
不断循环最终即可将执行任务顺序输出出来。
2、链式存储结构:
(1)初始化敢死队员链表:
通过循环创建链表节点,并将每个节点连接起来,并将最后一个结点连到头结点后的第一个元素,形成一个循环链表。
(2)执行任务:链式存储结构我将删除功能放到了执行任务的函数中,方便代码编写,减少代码量。由于链式存储结构无法随机存取,所以需要先通过循环遍历来找到开始数的队员,然后往后数四个,也就是从指定队员开始数数到第五个的队员执行任务,然后将其从链表中删除,进入循环后需往后数四个,然后到第五个时判断是否是排长且此时剩余的敢死队员除了排长是否还有其他人,如果都是则继续往后数一个执行任务,并将其删除,直到全部战士都执行任务为止结束循环。
六、系统实现
1、程序主界面截图
本次程序总共要3种命令,每种命令通过输入相应的数字选项就能实现。
2、关键部分的实现过程。
(1)顺序存储结构:
(2)链式存储结构
(3)输入错误时
(4)退出程序
七、结果分析
线性结构程序设计模块的这个敢死队任务执行模拟程序已实现了用顺序存储结构和链式存储结构来存储,此外用循环队列也可以来实现。
关于循环队列的操作由于只有对队头和队尾进行出队和入队的操作,所以循环队列的做法是不断从队头出队队员到队尾重新入队,直到数到该执行任务的队员就出队完直接输出执行任务而不用重新到队尾入队,不再参与下一次计数,这样也可以仅靠出队和入队操作来完成敢死队任务执行模拟程序。
顺序存储结构来实现的程序的时间复杂度为O(n2)。因为删除队员操作需要将删除队员的后面的队员往前移,时间复杂度为O(n),初始化队员逐个赋予编号也为O(n),而随机存取的时间复杂度为O(1),执行任务操作里有一个循环操作对所有队员进行执行任务,而其中又包含删除操作,时间复杂度为O(n2),所以这四个平均下来就是O(n2)。
而空间复杂度为O(n),因为需要n个存储空间来存储n个队员。
链式存储结构来实现的程序的时间复杂度为O(n)。因为初始化队员逐个赋予编号为O(n),删除操作只需O(1),找到开始队员为O(n),n个队员逐个执行任务也为O(n),这四个平均下来为O(n)。
而空间复杂度也为O(n),因为需要n个存储空间来存储n个队员。
对比这两种存储方式,空间复杂度都一样,而链式存储结构的时间复杂度更低,所以总结下来链式存储结构更适合这个敢死队任务执行模拟程序。经过了解,在实际开发时链式存储结构确实比顺序存储结构应用更加广泛,很少用顺序存储结构来存储的,因为效率相比较低。
八、实验心得
通过本次课程设计实现了用顺序存储结构和链式存储结构分别来实现敢死队任务执行模拟程序,本次课程设计通过一个例子来让我对这两种存储结构有了更深的了解,对代码编写也更加熟悉,同时对比了两种存储方式,我也感受到它们的不同之处,各有所长。
本次课程设计的亮点在于可以通过输入选择从哪一个敢死队员开始数,且对于输入都会有判错机制,在输入选项时会判断输入的是否在范围内,如果不在会提示重新输入,直到对为止,在输入从哪一个编号开始数也会判断这个输入的编号是否为正数且在敢死队员数量范围内,如果不合理会给出提示并重新输入,还有就是将两种存储结构放到同一个程序内通过主菜单来选择,这样方便比对结果。
这个敢死队任务执行模拟程序已实现了将执行任务的顺序逐个输出的功能,但仍存在一些问题,还可以再继续改进一下,比如增加一个功能,就是在轮到某个队员执行任务时,通过增加输入来判断他是否成功执行任务,如果成功则直接退出,否则继续轮到下一个,因为实际情况中有可能轮到某个队员时任务就执行成功了,但这种做法在敢死队队员数量过大时不太现实,需要对每个执行任务的队员都输入一次是否成功执行任务,这样会很麻烦,所以最终我的想法是在主菜单选择存储结构后,再继续选择是直接输出所有队员执行任务顺序,还是逐个输入选择是否执行任务成功从而可以直接提前结束程序。
九、源代码
C语言
#include <stdio.h>
#include <stdlib.h>
#define MAX_SOLDIERS 1000
//----------顺序存储结构----------
// 定义敢死队队员结构体
typedef struct
{
int number;
} Soldier_sq;
typedef struct
{
Soldier_sq soldiers[MAX_SOLDIERS];
int sum;
} SqList;
// 初始化敢死队队员顺序表
void initSoldiers_sq(SqList *L, int n)
{
int i;
L->sum = n;
for (i = 0; i < n; i++)
{
L->soldiers[i].number = i + 1;
}
}
// 删除敢死队员
void