矩阵的加法运算问题数据结构与算法课程设计报告.docx
《矩阵的加法运算问题数据结构与算法课程设计报告.docx》由会员分享,可在线阅读,更多相关《矩阵的加法运算问题数据结构与算法课程设计报告.docx(32页珍藏版)》请在冰豆网上搜索。
![矩阵的加法运算问题数据结构与算法课程设计报告.docx](https://file1.bdocx.com/fileroot1/2022-11/22/293d0138-4fea-4152-a7fa-c1b8349f25af/293d0138-4fea-4152-a7fa-c1b8349f25af1.gif)
矩阵的加法运算问题数据结构与算法课程设计报告
合肥学院
计算机科学与技术系
课程设计报告
2009~2010学年第二学期
课程
数据结构与算法
课程设计名称
矩阵的加法运算问题
学生姓名
胡九铭
学号
0804012039
专业班级
计算机科学与技术08级
(2)班
指导教师
王昆仑张贯虹
2010年6月
一、问题分析和任务定义
1、问题分析
此程序需要完成如下要求:
设计十字链表表示稀疏矩阵,并实现矩阵的加法运算。
并且要求能够要检查有关运算的条件,并对错误的条件产生报警。
2、
(1)设计函数建立稀疏矩阵。
(2)设计函数输出稀疏矩阵的值。
(3)构造函数进行两个稀疏矩阵相加,
(4)构造函数进行两个稀疏矩阵相加时是否能够符合运算要求,即检查有关运算的条件,并对错误的条件产生错误警报。
(5)登录函数,即需要口令才可以登录并使用计算器
(6)退出系统
3、原始数据的输入和输出格式
由于该问题是关于矩阵的运算方面的内容,所以输入时是对矩阵中的非零元分别进行插入矩阵中,输入格式是:
行、列、元素值,以数字的形式输入。
输出时,为了能更易于用户的观察和比较所以选用矩阵表的格式输出,这可以更加方便看出结果的正确与否,也有利于在编写程序的时候修改和改善程序的源代码。
4、算法应能实现的功能
该算法应能实现:
正确的用十字链表的存储结构建立数个稀疏矩阵;正确的输出用户建立的矩阵;能进行数个矩阵的相加运算;能对加法运算的运算条件进行判断并能对其产生报警提示。
5、该问题测试用例
(a)两个行列分别相同的矩阵
000900
500+520
009000
预测结果:
900
=1020
009
(b)两个行列数不相同的矩阵
3000000
0060
600+0000
2000
0000000
预测结果:
输出:
不能进行加法运算,矩阵的行数和列数应分别相同!
!
!
(c)两个以上的矩阵相加
0000010000
060000-3000
50300+00000+
0000000000
0000000000
00050
20000
10000
00000
05000
预测结果:
10050
23000
=60300
00000
05000
二、数据结构的选择和概要设计
1、主界面设计:
为了实现对稀疏矩阵的多种算法功能的管理,首先设计一个含有多个菜单项的主控菜单子程序以链接系统的各项子功能,方便用户交互式使用本系统。
本系统登录菜单运行界面如图1-1所示
图1-1
除了登录矩阵计算器菜单之外,还有计算器的选项菜单,如图1-2所示
图1-2
2、存储结构设计:
本系统采用十字链表结构存储稀疏矩阵的具体信息。
其中:
全部结点的信息用头结点为指针数组的链表存储;每个结点里面包含行号(i),列号(j)和对应的数值(v),它们是整型数据,还有两个指针cptr、rptr分别是行指针域和列指针域,属于node结构体。
全部的信息用结构体(CrossList)包含,包括指针数组(rhead和chead)和总共的行数(m),列数(n)以及非零元素的个数(t)。
3.系统功能设计:
本系统要完成稀疏矩阵的加法,就需要建立稀疏矩阵,则用Creat_CrossList()实现,然后要依次读取信息,即向Creat_CrossList()创建的空链表中插入结点,就是从键盘读取信息,行号,列号以及非零元素的值,这需要通过函数Insert_CrossList()来完成这个工作。
在该课程设计中,最主要的就是实现矩阵的加法,即矩阵A+B,该功能通过调用函数Matrixpuls_CrossList();来实现的。
要输出通过输出函数Display_CrossList();来完成。
但是在程序执行的过程中,调用相加函数之前,需要判断两个矩阵A与B是否符合矩阵相加的条件。
最后就是需要退出程序的操作,可以根据功能菜单选择进行操作,就可以退出程序。
4、模块设计:
本程序包含2个模块:
主程序模块和各功能实现模块。
调用关系如图1-3所示。
图1-3模块调用示意图
5、系统子程序及功能设计
(1)CrossList*Creat_CrossList()//创建矩阵
(2)CrossList*Insert_CrossList(CrossList*M)//往矩阵中插入结点信息
(3)voidDispaly_CrossList(CrossList*M)//输出矩阵信息
(4)CrossList*Matrixpuls_CrossList(CrossList*A,CrossList*B)//矩阵相加
(5)intJudge_CrossList(CrossList*A,CrossList*B)//判断矩阵是否符合相加条件
(6)voidLogin_Menu()//登录菜单
(7)voidLogin_Pcmd()//登录函数
(8)voidmain()//主函数,设置窗口模式和窗口标题,以及调用各个子函数
(9)Select_Menu()//计算器功能选择菜
三、详细设计和编码
1、数据类型的定义
//结点类型
typedefstructnode
{inti,j,v;//元素的行标、列标、值域
structnode*rptr,*cptr;//行指针域、列指针域
}OLNode;
//十字链表类型
typedefstruct
{OLNode*rhead[K],*chead[N];//存放行链表及列链表的首地址
intm,n,t;//矩阵的行数、列数、非灵元素个数
}CrossList;
2.系统主要子程序详细设计
(1)主程序模块设计及用户工作区模块设计
//设置窗口的大小,颜色,标题
system("cls");//清屏
system("color17");//设置窗口的背景颜色
system("modeconcols=60lines=25");//设置窗口模式
system("title欢迎使用矩阵加法计算器");//设置窗口标题
以后根据程序的提示执行操作,选择自己需要执行的操作
(2)以下是稀疏矩阵操作各子函数的定义:
①建立稀疏矩阵:
输入行数,然后判断行数是否符合要求,即行数是否大于0,若不符合则需要重新输入数据
printf("\n%c输入行数:
",1);//输入行数
scanf("%d",&(M->m));
while(M->m<=0)
{printf("\n%c您输入的行数不符实际...",1);
printf("\n\n%c请重新输入行数:
",1);
scanf("%d",&(M->m));
}
输入列数,然后判断列数是否符合要求,即列数是否大于0,若不符合则需要重新输入数据
printf("\n%c输入列数:
",1);//输入列数
scanf("%d",&(M->n));
while(M->n<=0)
{printf("\n%c您输入的列数不符实际...",1);
printf("\n\n%c请重新输入列数:
");
scanf("%d",&(M->n));}
输入非零元素的个数,然后判断个数是否符合要求,即列数是否大于等于0,若不符合则需要重新输入数据
printf("\n%c输入非零元素数个数:
",1);//输入非零元素个数
scanf("%d",&(M->t));
while(M->t<0)
{printf("\n%c您输入的非零元素数个数不符实际...",1);
printf("\n\n%c请重新输入非零元素数个数:
",1);
scanf("%d",&(M->t));}
判断创建矩阵的行数×列数>=非零元素的个数,比如5行3列,总共可以存储15个元素,而输入的时候可能会输入的数大于15,比如20,造成无法存储,这就需要重新创建矩阵。
while(M->t>(M->m*M->n))/
{printf("\n%c不符合条件...\n",1);
printf("\n");
printf("%c重要提示:
输入失败的原因是非零元素的个数>行数x列数\n",1);
printf("\n%c重新输入...",1);
printf("\n");printf("\n");printf("\n");printf("\n");
system("pause");//暂停
system("cls");//清屏
printf("\n%c输入行数:
",1);
scanf("%d",&(M->m));
while(M->m<=0)
{printf("\n%c您输入的行数不符实际...",1);
printf("\n\n%c请重新输入行数:
",1);
scanf("%d",&(M->m));}
printf("\n%c输入列数:
",1);
scanf("%d",&(M->n));
while(M->n<=0)
{printf("\n%c您输入的列数不符实际...",1);
printf("\n\n%c请重新输入列数:
",1);
scanf("%d",&(M->n));}
printf("\n%c输入非零元素数个数:
",1);
scanf("%d",&(M->t));
while(M->t<0)
{printf("\n%c您输入的非零元素数个数不符实际...",1);
printf("\n\n%c请重新输入非零元素数个数:
",1);
scanf("%d",&(M->t));}}
②输出稀疏矩阵:
通过for循环,来输出矩阵中的所有的非零元素的信息
for(a=1;a<=M->m;a++)
{p=M->rhead[a];
while(p!
=NULL)
{printf("\t%c<%d,%d,%d>\t",1,p->i,p->j,p->v);
p=p->rptr;}
printf("\n");}
③插入结点信息:
初始化行链表,列链表为空
for(i=1;i<=M->m;i++)//行链表置空
M->rhead[i]=NULL;
for(i=1;i<=M->n;i++)//列链表置空
M->chead[i]=NULL;
向创建的空矩阵中插入结点信息,不符合要求就需要重新输入。
比如输入的一个结点的信息的行号、列号大于行数或者列数。
printf("\n%c第%d个元素为:
",1,h);
scanf("%d%d%d",&p->i,&p->j,&p->v);
while(p->i>M->m||p->j>M->n)//判断输入是否符合要求
{printf("\n%c重要提示:
该元素的行号或者列号超出范围...",1);
printf("\n");
printf("\n%c重新输入第%d个元素:
",1,h);
scanf("%d%d%d",&p->i,&p->j,&p->v);
}
将结点插入到行链表的指针数组中,用数组rhead[]来存储
if((M->rhead[p->i]==NULL)||((M->rhead[p->i]->j)>(p->j)))//当该元素所在的行首地址为空或当前结点的列号比该列行链表中第一个结点的行号小
{p->rptr=M->rhead[p->i];//将该结点作为首元素结点插入到该行链表中
M->rhead[p->i]=p;}
else
//查找该行链表中的个结点的列号,将该结点按列号的顺序插入到该行链表中
{u=M->rhead[p->i];
q=M->rhead[p->i]->rptr;
while(q!
=NULL)//指针后移找到比待插入结点列号大的元素地址
{if(q->j>p->j)break;
u=q;q=q->rptr;}
p->rptr=q;//插入结点
u->rptr=p;}
将结点插入到列链表的指针数组中,用数组chead[]来存储
if((M->chead[p->j]==NULL)||((M->chead[p->j]->i)>(p->i)))//根据列号将结点插入到相应的列链表中
{p->cptr=M->chead[p->j];//将该结点作为首元素结点插入到该列链表中
M->chead[p->j]=p;}
else
{u=M->chead[p->j];
q=M->chead[p->j]->cptr;
while(q!
=NULL)
{if(q->i>p->j)break;
u=q;q=q->cptr;}
p->cptr=q;
u->cptr=p;}
④矩阵相加函数:
令a,b分别指向A和B的第一个行链表
i=1
a=A->rhead[i];//将矩阵A第i行的首地址赋给a
b=B->rhead[i];//将矩阵B第i行的首地址赋给b
并初始化指针acol[]
for(i=1;i<=A->n;i++)
acol[i]=A->chead[i];
逐行比较行链表A->rhead[i]与B->rhead[i]上的每一个结点*a和*b,直到链表B->rhead[i]中所有非0元结点均比较完毕:
ⅰ、若链表A->rhead[i]已比较完毕,或结点*b的列号小于结点*a的列号,则在链表A->rhead[i]中插入一个*b的复制结点:
if(a==NULL||a->j>b->j)//若a地址为空或列号大于b的列号
{if(pre==NULL)//a的前驱为空时
A->rhead[i]=p;//将p插入到该行头结点之前
else
pre->rptr=p;//将p插到a结点之前
p->rptr=a;
pre=p;//a的前驱变为p结点
}
同时结点*p也插入到相应的列链表中。
ⅱ、若结点*b的列号大于结点*a的列号,则在链表A->rhead[i]中查到列号大于*b的列号结点的前驱结点,并插入一个*b的复制结点*p;即若*c的后继结点的列号大于*b的列号,则*p插入到*c后:
while(a!
=NULL&&a->jj)
{pre=a;
a=a->rptr;}
if(a==NULL||a->j>b->j)//若a地址为空或列号大于b的列号
{pre->rptr=p;//将p插到a结点之前
p->rptr=a;
pre=p;//a的前驱变为p结点}
else//否则两结点列号相同
{a->v=a->v+b->v;//将两节点数值相加赋给a
if(a->v==0)//若相加后为值为0
{if(pre==NULL)//a的前驱为空时
A->rhead[i]=a->rptr;//将a结点删除
elsepre->rptr=a->rptr;//否则,将a的前驱后移一位
p=a;//p指向被删除的结点
a=a->rptr;//a后移一位
if(A->chead[p->j]==p)//p指向的结点为所在列的首元素时
{A->chead[p->j]=p->cptr;//将p从列链表中删除
acol[p->j]=p->cptr;}
else//找到p所在列链表的前驱结点
{while(acol[p->j]->cptr!
=p)
acol[p->j]=acol[p->j]->cptr;
acol[p->j]->cptr=p->cptr;}//将p从列链表中删除
free(p);}//释放p指向结点的空间
else//若相加后为值不为0
{pre=a;//pre指向a
a=a->rptr;}}//a后移一位
break;}
同时,结点*p也插入到相应的列链表中。
ⅲ、若结点*b的列号等于结点*a的列号,将*b结点的值加到*a结点上:
if(a!
=NULL&&a->j==b->j)//如果两结点列号相同
a->v=a->v+b->v;
此时,若a->v!
=0;则无需其他操作;否则,在十字链表A中删除该结点。
在行链表中删除该结点:
if(pre==NULL)//a的前驱为空时
A->rhead[i]=a->rptr;//将a结点删除
同时,在列地址A->chead[p->j]中删除该结点
若本行不是最后一行,则令a和b指向下一行行链表的首元素结点,转至
(2);否则,算法结束。
⑤判断函数:
intJudge_CrossList(CrossList*A,CrossList*B)
{if(A->m==B->m&&A->n==B->n)
return0;//符合返回0
elsereturn1;}//不符合返回1
该函数是用于判断矩阵A和矩阵B是否符合相加条件,即A和B的行数和列数是否相等,相等返回0,不相等返回1
四、上机调试
1、语法错误及修改:
出现的语法问题是要在于子函数和变量定义,括号的配对,关键字和函数名称的书写,以及一些库函数的规范使用。
这些问题均可以根据编译器的警告提示,对应的将其解决。
2、逻辑问题修改和调整:
①判断矩阵A和B的相加问题:
由于判断两矩阵是否符合相加条件,就是矩阵A和B的行数和列数是否相等,相等就执行加法运算,若不相等就需要修正矩阵A或者B,就需要重新创建,而在程序中没有加入
if(r=='A')//修改矩阵A
{printf("\n%c矩阵A...\n",1);
A=Creat_CrossList();
Insert_CrossList(A);}
if(r=='B')//修改矩阵B
{printf("\n%c矩阵B...\n",1);
B=Creat_CrossList();
Insert_CrossList(B);}
加入后就可以随时修改其中一个矩阵了,使其符合相加条件。
②在矩阵加法中,由于该程序是书上的,所以可以用现成的,但是在程序中有个问题,就是当a->v=a->v+b->v时,只有当相加结果为零的情况
if(a->v==0)//若相加后为值为0
{if(pre==NULL)//a的前驱为空时
A->rhead[i]=a->rptr;//将a结点删除
else
pre->rptr=a->rptr;//将a的前驱后移一位
p=a;//p指向被删除的结点
a=a->rptr;//a后移一位
if(A->chead[p->j]==p)//p指向的结点为所在列的首元素时
{A->chead[p->j]=p->cptr;//将p从列链表中删除
acol[p->j]=p->cptr;}
else//找到p所在列链表的前驱结点
{while(acol[p->j]->cptr!
=p)
acol[p->j]=acol[p->j]->cptr;
acol[p->j]->cptr=p->cptr;}//将p从列链表中删除
free(p);}//释放p指向结点的空间
而没有相加不等于零的情况,即a-v!
=0,因此需要加入如下函数
else//若相加后为值不为0
{pre=a;//pre指向a
a=a->rptr;}//a后移一位
五、测试结果及其分析
1、系统运行登陆界面如图1-4所示:
图1-4
2、系统的功能选项界面,如图1-5所示:
图1-5
3、创建矩阵A并输入矩阵A,如图1-6所示:
图1-6
4、创建矩阵B,并输入B,如图1-7所示:
图1-7
5、矩阵A+B,由上面矩阵A和B的截图可知,A与B不符合相加条件,产生错误提示并修正。
如图1-8所示:
图1-8
修改之后输出矩阵A+B的结果,如图1-9所示:
图1-9
6、退出程序,如图1-10所示:
图1-10
六、用户使用说明
本程序执行文件为:
矩阵的加法运算问题.exe
进入本系统之后,会显示登陆界面菜单,根据提示操作。
接着是进入欢迎登陆并使用矩阵加法计算器,然后按任意键继续,输入密码和账号,登陆即可以使用计算器了。
然后根据计算器界面的提示选择您想要的功能操作。
七、参考文献
[1]王昆仑,李红.数据结构与算法.北京:
中国铁道出版社,2006年5月。
[2]严蔚敏数据结构与算法视频
八、附录
#include"stdio.h"
#include"malloc.h"
#defineK50//预设稀疏矩阵的行数
#defineN50//预设稀疏矩阵的列数
#include"stdlib.h"
#include"string.h"
//结点类型
typedefstructnode
{inti,j,v;//元素的行标、列标、值域
structnode*rptr,*cptr;//行指针域、列指针域
}OLNode;
//十字链表类型
typedefstruct
{OLNode*rhead[K],*chead[N];//存放行链表及列链表的首地址
intm,n,t;//矩阵的行数、列数、非灵元素个数
}CrossList;
//创建空十字链表
CrossList*Creat_CrossList()
{CrossList*M;
M=(CrossList*)malloc(sizeof(CrossList));
printf("\n%c输入行数:
",1);//输入行数
scanf("%d",&(M->m));
while(M->m<=0)
{printf("\n%c您输入的行数不符实际...",1);
printf("\n\n%c请重新输入行数:
",1);
scanf("%d",&(M->m));}
printf("\n%c输入列数:
",1);//输入列数
scanf("%d",&(M->n));
while(M->n<=0)
{printf("\n%c您输入的列数不符实际...",1);
printf("\n\n%c请重新输入列数:
");
scanf("%d",&(M->n));}
printf("\n%c输入非零元素数个数:
",1);//输入非零元素个数
scanf("%d",&(M->t));
while(M->t<0)
{printf("\n%c您输入的非零元