神奇的酒杯课程设计报告书.docx
《神奇的酒杯课程设计报告书.docx》由会员分享,可在线阅读,更多相关《神奇的酒杯课程设计报告书.docx(16页珍藏版)》请在冰豆网上搜索。
神奇的酒杯课程设计报告书
一、题目:
(神奇的酒杯)小L在一次登山游玩不小心掉到了山底下摔断了右脚,然而庆幸的是竟让他发现了一个神奇的酒杯。
小L拿过酒杯摸一摸召唤出了酒杯里的大神,他求大神解救他的右脚。
但是大神就是大神,哪能随便施法呢,大神说:
你要是能解答我一个问题,我就搭救你。
问题是这样的:
已知有一棵树根为1,树上有N个结点编号为1~N,每个结点上有一个权值,大神可以随时改变某个结点的权值,他想求的是某棵子树上结点权值的最大值。
小L是个菜鸟,只恨算法没学好啊,只好把问题交给你了。
要求:
(1)输入第一行一个正整数N(2多组数据,N=0结束。
第二行为N个数,分别表示1~N的结点上的权值[1,100000000]接下来的N-1行,每行包含两个数u,v,表示u,v之间有一条边。
第N+2行输入为正整数M(0<=M<=100000)。
下面的M行输入为“C x y”或“Q x”。
(x为1到N的数)。
“C x y”表示将结点x的值改变成y(y>0)。
“Q x” 表示查询以x为根的子树上结点的权值的最大值。
(2)输出:
对每个查询输出相应的最大值。
(3)所设计的数据结构应尽可能节省存储空间。
(4)程序的运行时间应尽可能少。
二、问题分析:
此程序需要完成如下要求:
已知有一棵树根为1,树上有N个结点编号为1~N,每个结点上有一个权值,大神可以随时改变某个结点的权值,他想求的是某棵子树上结点权值的最大值。
实现本程序需要解决以下几个问题:
1、应该选择怎样的存储结构。
2、如何通过给定的二叉树的边信息,建立二叉树。
3、如何将结点x的权值改变成y。
4、怎样查询以x为根的子树上结点的权值的最大值。
5、怎样确定所需要的操作是改变权值还是查询权值的最大值。
输入要求:
输入第一行一个正整数N(2多组数据,N=0结束。
第二行为N个数,分别表示1~N的结点上的权值[1,100000000]接下来的N-1行,每行包含两个数u,v,表示u,v之间有一条边。
第N+2行输入为正整数M(0<=M<=100000)。
下面的M行输入为“C x y”或“Q x”。
(x为1到N的数)。
“C x y”表示将结点x的值改变成y(y>0)。
“Q x” 表示查询以x为根的子树上结点的权值的最大值。
输出要求:
对每个查询输出相应的最大值。
三、数据结构的选择和概要设计:
数据结构的选择
根据题目要求,存储结构既要保存二叉树边所连接的两个端点,又要保存二叉树中结点的权值。
所以,在这里我选择了静态链表的存储结构。
链表结点的地址表示二叉树结点的序号,链表结点中的data域表示二叉树结点的权值。
存储结构模型如下图:
1
lchild
data
rchild
2
lchild
data
rchild
.
lchild
data
rchild
n
lchild
data
rchild
概要设计:
1、如何通过给定的二叉树的边信息,建立二叉树。
在输入时,二叉树的边的信息用这条边所连接的两个端点的序号(a,b)表示。
在建立二叉树的函数中,将结点b的地址赋值给结点a的孩子结点指针域。
2、如何将结点x的权值改变成y。
对于将结点x中的权值改变为y这个操作非常简单,可以直接将结点x中的data域赋值为y即可。
3、怎样查询以x为根的子树上结点的权值的最大值。
对于这个问题,可以采用遍历的方法,并找出最大值。
当要查询以x为根的子树上结点的权值时,将结点x的地址作为参数输入到遍历函数中,调用先序递归遍历函数。
并设置一个全局的静态变量max,用来找出子树中权值的最大值。
设max初始值为0,遍历子树中的权值。
当max小于这个结点的权值时,将max的值赋值为该结点的权值;当max不小于这个结点的权值时,不做任何操作。
4、怎样确定所需要的操作是改变权值还是查询权值的最大值。
对于这个问题,可以设置一个菜单选项。
根据输入决定将要做的操作。
程序包含的函数有:
主函数:
main();
建二叉树函数:
bitree*creatree();
改变指定结点的权值函数:
bitree*change_bt(intx,inty);
查询以x为根的子树上结点的权值的最大值函数:
voidpreorder(bitree*t);
程序的流程图如下:
(c)(q)
(t)
4、算法思想:
此程序中最主要的问题就是,如何建立二叉树和查询以x为根的子树上结点的权值的最大值。
建二叉树的算法思想:
按提示输入二叉树的结点个数,接着再按提示输入二叉树的结点权值。
此程序的核心在于,当输入边的信息时,建立二叉树。
在这,我的算法思想是,当输入的边的信息是(a,b)时,首先检查结点a的左孩子的指针域是否为空,若为空,则将a的左孩子的指针域赋值为结点b的地址。
操作如下:
for(i=0;i{
scanf("%d%d",&a,&b);
if(bt[a-1].lchild==NULL)
bt[a-1].lchild=&bt[b-1];
else
bt[a-1].rchild=&bt[b-1];
}
查询以x为根的子树上结点的权值的最大值的算法思想:
对于这个问题,采用遍历的方法,并找出最大值。
当要查询以x为根的子树上结点的权值时,将结点x的地址作为参数输入到遍历函数中,调用先序递归遍历函数。
并设置一个全局的静态变量max,用来找出子树中权值的最大值。
设max初始值为0,遍历子树中的权值。
当max小于这个结点的权值时,将max的值赋值为该结点的权值;当max不小于这个结点的权值时,不做任何操作。
操作如下:
t=&bt[x-1];
if(t)
{
if(maxdata)
max=t->data;
preorder(t->lchild);
preorder(t->rchild);
}
5、详细设计和主要代码段:
1、存储结构的确定:
根据题目要求,存储结构既要保存二叉树边所连接的两个端点,又要保存二叉树中结点的权值。
所以,在这里我选择了静态链表的存储结构。
设计如下:
typedefstructnode
{
intdata;
node*lchild,*rchild;
}bitree;
2、首先需要解决的问题是如何按照题目的要求建立二叉树:
按提示输入二叉树的结点个数,接着再按提示输入二叉树的结点权值。
此程序的核心在于,当输入边的信息时,建立二叉树。
在这,我的设计是,当输入的边的信息是(a,b)时,首先检查结点a的左孩子的指针域是否为空,若为空,则将a的左孩子的指针域赋值为结点b的地址。
详细设计流程图如下:
for(i=0;i{scanf("%d",&bt[i].data);}
(a,b)
YN
源代码如下:
bitree*creatree()
{
inti,a,b;
printf("输入二叉树的结点个数!
\n");
scanf("%d",&n);
printf("输入结点的权值(%d个)!
\n",n);
for(i=0;i{
scanf("%d",&bt[i].data);
bt[i].lchild=bt[i].rchild=NULL;
}
printf("输入二叉树边的关系(%d个)!
\n",(n-1));
for(i=0;i{
scanf("%d%d",&a,&b);
if(bt[a-1].lchild==NULL)
bt[a-1].lchild=&bt[b-1];
else
bt[a-1].rchild=&bt[b-1];
}
returnbt;
}
3、改变指定结点的权值函数:
对于将结点x中的权值改变为y这个操作非常简单,可以直接将结点x中的data域赋值为y即可。
源代码如下:
bitree*change_bt(intx,inty)
{
bt[x-1].data=y;
returnbt;
}
4、查询以x为根的子树上结点的权值的最大值函数:
对于这个问题,采用遍历的方法,并找出最大值。
当要查询以x为根的子树上结点的权值时,将结点x的地址作为参数输入到遍历函数中,调用先序递归遍历函数。
并设置一个全局的静态变量max,用来找出子树中权值的最大值。
设max初始值为0,遍历子树中的权值。
当max小于这个结点的权值时,将max的值赋值为该结点的权值;当max不小于这个结点的权值时,不做任何操作。
YN
并且递归调用函数。
源代码如下:
voidpreorder(bitree*t)
{
if(t)
{
if(maxdata)
max=t->data;
preorder(t->lchild);
preorder(t->rchild);
}
}
6、上机调试情况记录
1、语法错误及其修改:
出现的语法问题主要在于子函数和变量的定义,括号的配对,关键字和函数名称的书写,和一些库函数的规范使用。
这些问题均可以根据编译器的警告提示,对应的将其解决。
2、逻辑问题的修改和调整:
在递归调用preorder(bitree*t)函数时,max没有设置为静态变量。
使得程序每次递归调用时都被初始化为0,使得程序实现不了查询以x为根的子树上结点的权值的最大值的功能。
后来将max修改为全局静态变量后,程序每次递归调用函数时,max不会再次被初始化,使得程序实现了查询以x为根的子树上结点的权值的最大值功能。
3、空间性能的调整:
在preorder(bitree*t)函数中,一开始使用的是,先定义了一个全局数组Z_C[m],存储遍历时的data域的值,然后再在Z_C[m]数组中找出最大的权值。
后来考虑到这样浪费了m个int型的存储空间。
决定使用一个静态变量max。
设max初始值为0,遍历子树中的权值。
当max小于这个结点的权值时,将max的值赋值为该结点的权值;当max不小于这个结点的权值时,不做任何操作。
7、测试用例、结果及其算法性能分析
当输入的数据为:
结点数为3
权值为231645
边的信息为:
12
13
操作为:
Q2
Q3
C234
Q2
当输入的数据为:
结点数为6
权值为2316456356
边的信息为:
12
23
24
45
46
操作为:
Q5
Q3
C512
Q2
当输入的数据为:
结点数为3
权值为231645
边的信息为:
12
23
操作为:
Q2
Q3
C234
Q2
算法性能分析:
1、时间性能:
本程序的时间性能较好。
所有函数中仅有一层for循环,没有任何嵌套循环,所以本程序的时间性能为O(n)。
2、空间性能:
解决本问题,采用的是静态链表,所以空间性能为O(n)。
除此以外,还定义了全局变量n,用来存放结点的个数;还有,定义了一个全局静态变量max。
所以此程序的空间性能还是O(n)。
八、用户使用说明
本程序在运行过程中带有提示性语句。
由于本程序需要输入的数据比较多,输入时要按照提示进行,如果输入错误,提示输入错误并不录入。
所以,在运行时,只要按照提示输入就行了。
9、参考文献
[1]王昆仑,李红.数据结构与算法.北京:
中国铁道出版社,2006年5月。
[2]李红,王昆仑."数据结构与算法"实验指导合肥:
合肥学院教务处2006年5月。
10、附录(完整源程序)
#include
#definem20
staticintmax=0;
intn;
typedefstructnode
{
intdata;
node*lchild,*rchild;
}bitree;
bitreebt[m];
bitree*creatree()
{
inti,a,b;
printf("输入二叉树的结点个数!
\n");
scanf("%d",&n);
printf("输入结点的权值(%d个)!
\n",n);
for(i=0;i{
scanf("%d",&bt[i].data);
bt[i].lchild=bt[i].rchild=NULL;
}
printf("输入二叉树边的关系(%d个)!
\n",(n-1));
for(i=0;i{
scanf("%d%d",&a,&b);
if(bt[a-1].lchild==NULL)
bt[a-1].lchild=&bt[b-1];
else
bt[a-1].rchild=&bt[b-1];
}
returnbt;
}
bitree*change_bt(intx,inty)
{
bt[x-1].data=y;
returnbt;
}
voidpreorder(bitree*t)
{
if(t)
{
if(maxdata)
max=t->data;
preorder(t->lchild);
preorder(t->rchild);
}
}
voidmain()
{
inta,b,n;
charch;
creatree();
while
(1)
{
printf("输入操作方式(c/q/t)!
\n");
scanf("%s",&ch);
if(ch=='c')
{
printf("请输入需要改变的结点和权值(a,b)!
\n");
scanf("%d%d",&a,&b);
change_bt(a,b);
}
if(ch=='q')
{
printf("输入想要查询的子树的根结点序号!
\n");
scanf("%d",&n);
bitree*t;
t=&bt[n-1];
preorder(t);
printf("最大的权值为%d\n",max);
max=0;
}
if(ch=='t')
break;
}
}