数据结构课程设计修道士野人问题和西文图书管理系统.docx
《数据结构课程设计修道士野人问题和西文图书管理系统.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计修道士野人问题和西文图书管理系统.docx(42页珍藏版)》请在冰豆网上搜索。
数据结构课程设计修道士野人问题和西文图书管理系统
题号题目3、修道士与野人问题
1、需求分析
n个修道士和n个野人渡河,只有一条小船,能容纳c人,两种人都会划船,建立过河方式。
满足:
野人无法侵犯修道士。
这就要求无论在何处,修道士的个数不得少于野人的人数(除非修道士个数为0)。
设计程序模拟该过程。
程序的输入为修道士(野人)的个数以及每条船容纳人的个数。
输出为判断是否可以安全渡河。
如果能,则给出一个小船来回次数最少的最佳方案。
要求:
(1)用一个三元组(x1,x2,x3)表示渡河过程中各个状态。
其中,x1表示起始岸上修道士个数,x2表示起始岸上野人个数,x3表示小船位置(0——在目的岸,1——在起始岸)。
例如(5,3,0)表示起始岸上有5个修道士,3个野人,小船在目的岸一边。
(2)采用邻接表做为存储结构。
最短路径搜索采用广度搜索法。
(3)输出最优解
若问题有解(能渡过河去),则输出一个最佳方案。
用三元组表示渡河过程中的状态,并用箭头指出这些状态之间的迁移:
目的状态←…中间状态←…初始状态。
若问题无解,则给出“渡河失败”的信息。
(4)求出所有的解。
2、设计
2.1设计思想
(1)数据结构设计:
根据题目要求,用图形结构,用邻接表来存储结点,以及结点之间的关系,同时在广度优先遍历中利用到队列。
(2)算法设计:
先定义一个图利用邻接表储存结构,再举出在船上修道士和野人的所有情况,然后判断其修道士是否处于安全的状态,如果安全则将该点添加到图中,点添加完后在看两点之间是否连通如果连通则可将边添加到图中,这样就创建出了图,然后分别利用广度搜索和深度搜索来完成题目的要求。
2.2设计表示
(1)函数调用关系图
(2)函数接口规则说明
intSearch(AdjLGraph*G,intx,inty,intm)/*查找起始和最后的结点,其中x,y,m分别表示起始岸修道士人数,野人人数和船的状态*/
intChecking(DataTypex)/*检查修道士是否安全,x表示邻接表中的一个结点*/
intConnect(AdjLGraph*G,inti,intj)/*将能走通的点连接起来,i,j为图中的两个结点*/
voidCreat(AdjLGraph*G)/*图的创建*/
intPrint(AdjLGraphG)/*从后向前打印最短路径*/
intBroadFSearch(AdjLGraphG,intu,intv)/*用广度优先遍历搜索最短路径,u表示起始结点,v表示最后的结点*/
voidPrint1(AdjLGraphG)/*打印输出所有最短路径*/
voidDFS(AdjLGraphG,intu,intv,intvisited[])/*利用深度搜索找出所有最短路径,u表示起始结点,v表示最后的结点,visited[]用来标记结点是否被访问*/
2.3详细设计
首先是定义邻接表
typedefstruct
{
intdaoshi;//道士
intyeren;//野人
intship;//船的位置
}DataType;
typedefstructNode//边结点的结构体
{
intdest;//邻接边的弧头顶点序号
structNode*next;//单链表的下一个结点指针
}Edge;
typedefstruct
{
DataTypedata;//顶点数据元素
intsource;//弧尾顶点序号
Edge*adj;//邻接边的头指针
}AdjLHeight;
typedefstruct
{
AdjLHeighta[200];//邻接表数组
intnumOfVerts;//顶点个数
intnumOfEdges;//边个数
}AdjLGraph;//邻接表结构体
同时定义了几个全局变量,便于函数的直接利用
intn,c;//修道士和野人人数为n,船能容纳人数为c
intPath[200];//保存结点的后驱结点
intPath1[200];//保存结点的前驱结点
利用上述结构和相关函数可以构造出图,然后对图进行利用;
然后在广度优先遍历搜索中用到了队列
typedefstruct
{
intqueue[200];
intrear;
intfront;
intcount;
}SeqCQueue;
最后通过主函数main调用各函数得到相关信息
intmain()
{
AdjLGraphG;
intvisited[200];//标记结点是否被访问
printf("输入小船可装的人数:
\n");
while(scanf("%d",&c)!
=EOF)
{
memset(Path,0,sizeof(0));
memset(visited,0,sizeof(visited));
memset(Path1,0,sizeof(Path1));
N=0;
printf("输入野人或修道士的个数:
\n");
scanf("%d",&n);
AdjInitiate(&G);
Creat(&G);
v1=Search(&G,n,n,1);
v2=Search(&G,0,0,0);
if(BroadFSearch(G,v1,v2)==0)
printf("渡河失败\n");
else{
printf("输出所有最短路径的情况:
\n");
DFS(G,v1,v2,visited);
printf("共有%d种情况\n",N);
}
printf("输入小船可装的人数:
\n");
}
return0;
}
3、调试分析
在进行运行的时候,曾出现了打印输出错误,经过一步一步调试,发现在插入结点的时候出现了插入错误,即没有考虑到结点后驱的改变,通过改正,重新运行检测,运行结果正确,在排版时通过一步步调试,能够使输出结果很明显的表示的船的方案。
4、用户手册
在VC下运行,很简单的输入,只需输入野人和道士的人数N和船能承载的人的个数C即可。
5、测试数据及测试结果
(1)输入修道士和野人的人数n=6,船能容纳的人c=2,;不能安全渡河;
测试结果:
(2)输入修道士和野人人数为n=3,船能容纳的人c=2;渡河成功
测试结果:
输出一种最短路径
输出所有最短路径
输出最短路径数目:
6、源程序清单
#include
#include
#include
#include
intn,c;//修道士和野人人数为n,船能容纳人数为c
intPath[200];//保存结点的后驱结点
intPath1[200];//保存结点的前驱结点
typedefstruct
{
intdaoshi;//道士
intyeren;//野人
intship;//船的位置
}DataType;
typedefstructNode//边结点的结构体
{
intdest;//邻接边的弧头顶点序号
structNode*next;//单链表的下一个结点指针
}Edge;
typedefstruct
{
DataTypedata;//顶点数据元素
intsource;//弧尾顶点序号
Edge*adj;//邻接边的头指针
}AdjLHeight;
typedefstruct
{
AdjLHeighta[200];//邻接表数组
intnumOfVerts;//顶点个数
intnumOfEdges;//边个数
}AdjLGraph;//邻接表结构体
voidAdjInitiate(AdjLGraph*G)//初始化
{
inti;
G->numOfEdges=0;//图的边数
G->numOfVerts=0;//顶点数
for(i=0;i<200;i++)
{
G->a[i].source=i;
G->a[i].adj=NULL;
}
}
voidInsertVertex(AdjLGraph*G,inti,DataTypex)//插入顶点
{
if(i>=0&&i<200)
{
G->a[i].data.daoshi=x.daoshi;//修道士人数
G->a[i].data.yeren=x.yeren;//野人人数
G->a[i].data.ship=x.ship;//船的状态
G->numOfVerts++;
}
elseprintf("顶点越界\n");
}
voidInsertEdge(AdjLGraph*G,inti,intj)//插入边
{
Edge*p;
if(i<0||i>=G->numOfVerts||j<0||j>=G->numOfVerts)
{
printf("参数i或j越界出错!
\n");
return;
}
p=(Edge*)malloc(sizeof(Edge));//申请邻接边单链表结点空间
p->dest=j;//置邻接边弧头序号
p->next=G->a[i].adj;//
G->a[i].adj=p;
G->numOfEdges++;
}
voidAdjDestroy(AdjLGraph*G)//释放指针内存空间
{
inti;
Edge*p,*q;
for(i=0;inumOfVerts;i++)//依次释放内存空间
{
p=G->a[i].adj;
while(p!
=NULL)
{
q=p->next;
free(p);
p=q;
}
}
}
//队列
typedefstruct
{
intqueue[200];
intrear;
intfront;
intcount;
}SeqCQueue;
//初始化
voidQueueInitiate(SeqCQueue*Q)
{
Q->rear=0;
Q->front=0;
Q->count=0;
}
//非空否
intQueueNotEmpty(SeqCQueueQ)
{
if(Q.count!
=0)return1;
elsereturn0;
}
//入队列
intQueueAppend(SeqCQueue*Q,intx)
{
if(Q->count>0&&Q->rear==Q->front)
{
printf("队列已满无法插入!
\n");
return0;
}
else
{
Q->queue[Q->rear]=x;
Q->rear=(Q->rear+1)%200;
Q->count++;
return1;
}
}
//出队列
intQueueDelete(SeqCQueue*Q,int*d)
{
if(Q->count==0)
{
printf("队列已空无数据元素出队列!
\n");
return0;
}
else
{
*d=Q->queue[Q->front];
Q->front=(Q->front+1)%200;
Q->count--;
return1;
}
}
//取队头数据元素
intQueueGet(SeqCQueueQ,int*d)
{
if(Q.count==0)
{
printf("队列已空无数据元素可取!
\n");
return0;
}
else
{
*d=Q.queue[Q.front];
return1;
}
}
//查找起始和最后的结点
intSearch(AdjLGraph*G,intx,inty,intm)
{
inti;
for(i=0;inumOfVerts;i++)
if(G->a[i].data.daoshi==x&&G->a[i].data.yeren==y
&&G->a[i].data.ship==m)
returni;
return-1;
}
intChecking(DataTypex)//检查修道士是否安全
{
if((x.daoshi>=x.yeren||x.daoshi==0)&&((n-x.daoshi)>=(n-x.yeren)||x.daoshi==n)&&
x.daoshi>=0&&x.daoshi<=n&&x.yeren>=0&&x.yeren<=n)
return1;
elsereturn0;
}
//将能走通的点连接起来
intConnect(AdjLGraph*G,inti,intj)
{
intm;
m=(G->a[i].data.daoshi-G->a[j].data.daoshi)+(G->a[i].data.yeren-G->a[j].data.yeren);
if(G->a[i].data.ship==0&&G->a[j].data.ship==0||G->a[i].data.ship==1&&G->a[j].data.ship==1)
return0;//两个结点都在此岸或都在对岸,不连通
elseif(G->a[i].data.ship==1&&G->a[j].data.ship==0)//从起始岸到目的岸人数要减少
{
if(G->a[j].data.daoshi>G->a[i].data.daoshi||G->a[j].data.yeren>G->a[i].data.yeren)
//----------如果J结点的道士或野人的个数比开始船没从I开到J时还大时
return0;
if(G->a[j].data.daoshi==G->a[i].data.daoshi&&G->a[j].data.yeren==G->a[i].data.yeren)
return0;
if(m>c)return0;//------------船上的人超重
return1;
}
elseif(G->a[i].data.ship==0&&G->a[j].data.ship==1)
{
//-----------------------从终点到起始最后人应该有所增加
if(G->a[j].data.daoshia[i].data.daoshi||G->a[j].data.yerena[i].data.yeren)
//----------如果J结点的道士或野人的个数比开始船没从I开到J时还小时
return0;
if(G->a[j].data.daoshi==G->a[i].data.daoshi&&G->a[j].data.yeren==G->a[i].data.yeren)
return0;
if((-1)*m>n)return0;//------------船上的人超重
return1;
}
return0;
}
//创建图
voidCreat(AdjLGraph*G)
{
inti,j,k,m;
m=0;
DataTypex;
for(k=0;k<=1;k++)//船有两种情况,在对岸和在起始岸
{
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
{
x.daoshi=i;
x.yeren=j;
x.ship=k;
if(Checking(x)==1)
{
InsertVertex(G,m,x);
m++;
}
}
}
for(i=0;inumOfVerts;i++)
for(j=0;jnumOfVerts;j++)
if(Connect(G,i,j))
{
InsertEdge(G,i,j);
}
}
intN;//渡河成功的总次数
intM;//最短路径
intv1;//起始点
intv2;//终点
//从后向前打印最短路径
intPrint(AdjLGraphG)
{
intk,i=0;
printf("(%d%d%d)<-----",G.a[v2].data.daoshi,G.a[v2].data.yeren,G.a[v2].data.ship);
for(k=Path1[v2];k!
=v1;k=Path1[k])
{
i++;
printf("(%d%d%d)<------",G.a[k].data.daoshi,G.a[k].data.yeren,G.a[k].data.ship);
}
printf("(%d%d%d)\n",G.a[v1].data.daoshi,G.a[v1].data.yeren,G.a[v1].data.ship);
returni;
}
//用广度优先遍历搜索最短路径
intBroadFSearch(AdjLGraphG,intu,intv)
{
Edge*p;
intvisit[200];//标记点是否被访问
intw,z;
memset(visit,0,sizeof(visit));//对数组visit[200]清零
SeqCQueueQ;
visit[u]=1;
QueueInitiate(&Q);
QueueAppend(&Q,u);
while(QueueNotEmpty(Q))
{
QueueDelete(&Q,&w);
p=G.a[w].adj;
while(p!
=NULL)
{
z=p->dest;
if(z==v)
{
Path1[z]=w;//---------------保存前驱
printf("输出一种最短路径的情况\n");
M=Print(G);
return1;
}
elseif(!
visit[z])//结点j未被访问过
{
Path1[z]=w;
visit[z]=1;
QueueAppend(&Q,z);
}
p=p->next;
}
}
return0;
}
voidPrint1(AdjLGraphG)
{
intk,j;
inti=0;
DataTypea1[200];
memset(a1,0,sizeof(a1));
for(k=Path[v1];k!
=v2;k=Path[k])
{
a1[0]=G.a[v1].data;
a1[i+1]=G.a[k].data;
i++;
}
if(i==M)
{
a1[i+1]=G.a[v2].data;
N++;
printf("(%d%d%d)",G.a[v1].data.daoshi,G.a[v1].data.yeren,G.a[v1].data.ship);
for(j=1;j<=M+1;j++)
{
printf("-->(%d%d)-->(%d%d%d)",abs(a1[j].daoshi-a1[j-1].daoshi),abs(a1[j].yeren-a1[j-1].yeren),a1[j].daoshi,a1[j].yeren,a1[j].ship);
}
printf("\n");
}
}
//利用深度搜索找出所有最短路径
voidDFS(AdjLGraphG,intu,intv,intvisited[])
{
Edge*p;
intw;
visited[u]=1;
p=G.a[u].adj;
while(p!
=NULL)
{
w=p->dest;
if(w==v)
{
Path[u]=w;//-----记下当前结点的后驱
Print1(G);
}
elseif(visited[w]==0){
Path[u]=w;
//找后驱,保存下来
DFS(G,w,v,visited);
}
p=p->next;
}
visited[u]=0;
}
intmain()
{
AdjLGraphG;
intvisited[200];//标记结点是否被访问
printf("输入小船可装的人数:
\n");
while(scanf("%d",&c)!
=EOF)
{
memset(Path,0,sizeof(0));
memset(visited,0,sizeof(visited));
memset(Path1,0,sizeof(Path1));
N=0;
printf("输入野人或修道士的个数:
\n");
scanf("%d",&n);
AdjInitiate(&G);
Creat(&G);
v1=Search(&G,n,n,1);
v2=Search(&G,0,0,0);
if(BroadFSearch(G,v1,v2)==0)
printf("渡河失败\n");
else{
printf("输出所有最短路径的情况:
\n");
DFS(G,v1,v2,visited);
printf("共有%d种情况\n",N);
}
printf("输入小船可装的人数:
\n");
}
return0;
}
题号题目6、西文图书管理系统
1、需求分析
图书管理基本业务活动包括:
对一本书的采编入库、清除库存、借阅和归还等等。
试设计一个图书管理系统,将上述业务活动借助于计算机系统完成。
要求:
(1)每种书的登记内容至少包括书号、书名、著者、现存量和总库存量等五项。
(2)作为演示系统,不必使用文件,全部数据可以都在内存存放。
要用B-树(4阶树)对书号建立索引,以获得高效率。
(3)系统应有以下功能:
采编入库、清除库存、借阅、归还、显示(以凹入表的形式显示)等。
2、设计
2.1设计思想
(1)数据结构设计:
本题要求用B_树来进行存储,其逻辑结构为树形结构,B_树有利于关键字的查找,是一种平衡多叉排序树。
(2)算法设计:
将比关键字小的元素插到该关键字的左孩子结点中,比关键字大的的元素插到该关键字的右孩子结点中,当一个结点中关键字的个数超过规定的个数时,该节点要进行分裂,并且保证所有的叶子结点都在同一层。
2.2设计表