我第一下拿到这个题目,第一反应就是先定义好数据结构,然后构建好十字链表基础操作的函数,也就是“创插遍历”这些操作。下面是我的定义和函数操作。
typedef int ElemType;
typedef struct OLNode{
int i,j;//行号和列号
ElemType elem;
struct OLNode *right;//右边元素
struct OLNode *down;//下边元素
}OLNode,*OLink;typedef struct CrossList{
OLink* rHead;
OLink* cHead;
int mu,nu,tu;//行数、列数、非零元个数
}CrossList;
CrossList* CreateOLSMatrix(int r,int c,int t){//创建目标十字链表的表头
CrossList* p=(CrossList*)malloc(sizeof(CrossList));
if(p==NULL){
printf("Out of space!");
}
p->mu=r;
p->nu=c;
p->tu=t;
p->rHead=(OLink*)malloc((r+1)*sizeof(OLink));
p->cHead=(OLink*)malloc((c+1)*sizeof(OLink));
if(p->rHead==NULL||p->cHead==NULL) printf("Out of space!\n");
for(int i=0;i<r+1;i++){
p->rHead[i]=NULL;
}//强迫症,初始化操作
for(int i=0;i<c+1;i++){
p->cHead[i]=NULL;
}//强迫症,初始化操作
return p;
}void InsertMatrix(CrossList* p,int i,int j,int elem){//插入数据
OLink p1=(OLink)malloc(sizeof(OLNode));
if(p1==NULL) printf("Out of space!\n");
p1->i=i;p1->j=j;p1->elem=elem;
//创建并填充结点
if(p->rHead[i]==NULL||p->rHead[i]->j>j){
p1->right=p->rHead[i];
p->rHead[i]=p1;
}else{
OLink q=p->rHead[i];
while(1){
if(q->right==NULL||q->right->j>j) break;
q=q->right;
}//找到插入位置
p1->right=q->right;
q->right=p1;
}//完成行插入
if(p->cHead[j]==NULL||p->cHead[j]->i>i){
p1->down=p->cHead[j];
p->cHead[j]=p1;
}else{
OLink q=p->cHead[j];
while(1){
if(q->down==NULL||q->down->i>i) break;
q=q->down;
}//找到插入位置
p1->down=q->down;
q->down=p1;
}//完成列插入
}int BuildMatrix(CrossList* p){//完善表
for(int k=1;k<=p->tu;k++){
int i,j,elem;
scanf("%d%d%d",&i,&j,&elem);
InsertMatrix(p,i,j,elem);
}
}int OutputMatrix(CrossList* p){//输出表
for(int i=1;i<=p->mu;i++){
OLink q=p->rHead[i];
for(int j=1;j<=p->nu;j++){
if(q!=NULL&&j==q->j){
printf("%d %d %d\n",q->i,q->j,q->elem);
q=q->right;
}
}
}//按照先行后列的顺序输出
}
其中插入数据这个函数有点难。
我们在插入元素时,既要完成行插入,又要完成列插入。虽然这两个步骤基本类似,但我还是建议大家不要看行插入步骤,独立敲一遍列插入步骤。记住,不要着急,不要浮躁,静下心来,不要害怕说你同学就因此速度比你快,然后他就能做更多的练习,最后绩点比你高,拿了奖学金最后跟你炫耀,打击挖苦你,嘲讽你。大学里面最可恶的不是不学习的人,而是那些”欻欻欻写完作业,然后把你搞得浮躁,搞崩你的心态,最后嘲讽你不如他,还拿到了奖学金”的人。(就和考理综的时候,第一考场总有人来回翻卷子,就是为了搞崩你的心态)。不要慌,稳下来,因为你一慌,他们的目的就达到了。再回到这道题,如果你复制粘贴过来,很多东西你会忘记调整为列,而且Debug的时候,大家的第一反应都是先关注行,所以忽视了列,如果列插入上出了问题,是很难很难找出来错误的,不信大家看我下面的例子:
if(p->cHead[j]==NULL||p->cHead[j]->i>i){
p1->down=p->rHead[j];//1
p->rHead[j]=p1;//2
}else{
OLink q=p->rHead[j];//3
while(1){
if(q->down==NULL||q->down->i>i) break;
q=q->down;
}//找到插入位置
p1->down=q->down;
q->down=p1;
}//完成列插入
上面3处就是我复制过来以后,忘记调整过来的地方,如果只输出i=2这一行的第2个元素,结果是下面这样,根本就不正确。
3 3 3
1 0 2
2 1 2
2 2 3--------------------------------
Process exited after 16.53 seconds with return value 3221225477
请按任意键继续. . .
正确的代码应该把这3处给改掉,像下面这样:
if(p->cHead[j]==NULL||p->cHead[j]->i>i){
p1->down=p->cHead[j];//1
p->cHead[j]=p1;//2
}else{
OLink q=p->cHead[j];//3
while(1){
if(q->down==NULL||q->down->i>i) break;
q=q->down;
}//找到插入位置
p1->down=q->down;
q->down=p1;
}//完成列插入
最后,也就是咱们程序的核心:“矩阵相加”了:
int AddMatrix(CrossList* p1,CrossList* p2,CrossList* p3){//矩阵相加得到新矩阵p3
for(int i=1;i<=p1->mu;i++){
//先行后列,一点一点地来做
OLink temp1=p1->rHead[i];
OLink temp2=p2->rHead[i];
for(int j=1;j<=p1->nu;j++){
int flag1=temp1!=NULL&&j==temp1->j;
int flag2=temp2!=NULL&&j==temp2->j;
//为什么我要用flag呢?因为都放在if后面的话,if会变的很长很长,超级麻烦
if(flag1==1&&flag2!=1){//如果这个位置上,p1有数据,p2没数据
InsertMatrix(p3,temp1->i,temp1->j,temp1->elem);
temp1=temp1->right;
}
if(flag1!=1&&flag2==1){//如果这个位置上,p1没数据,p2有数据
InsertMatrix(p3,temp2->i,temp2->j,temp2->elem);
temp2=temp2->right;
}
if(flag1==1&&flag2==1){//如果这个位置上,p1有数据,p2也有数据
int total=temp1->elem+temp2->elem;
if(total==0){
p3->tu=p3->tu-2;
//如果p1的数据与p2的数据相加为0,那就不用插入
//别忘了p3中非零元素要减2
}else if(total!=0){
InsertMatrix(p3,temp1->i,temp1->j,total);
p3->tu--;
}//如果相加和不为0,那么把和total插入。
//别忘了p3中非零元素要自减
temp1=temp1->right;
temp2=temp2->right;
}
}
}
}
最后总结一下,有一些特别容易错的地方,而且不容易Debug出来:
1.插入元素时,既要完成行插入,又要完成列插入。虽然这两个步骤基本类似,但还是建议大家不要看行插入步骤,独立敲一遍列插入步骤。
2.注意三元组表的行数是从1开始,列数也是从1开始,所以在设置for循环时,要注意应该设置成:
for(int i=1;i<=...;i++)
不应该设置成:
for(int i=0;i<...;i++)
这也就是为什么,在对CrossList型变量的rHead动态分配空间时,要多分配1个的原因,就是因为下标为0的位置不可以使用。
p->rHead=(OLink*)malloc((r+1)*sizeof(OLink));
3.在进行矩阵相加时,特殊情况优先考虑,即优先考虑“矩阵p1和p2在i行j列上都有元素”的情况,这个时候就要根据相加和来判断了。如果相加和为0,该怎么处理;如果相加和不为0,又该怎么处理,不管你怎么做,都要记住一点:"temp1=temp1->right;temp2=temp2->right;"
4.不要忘记考虑相加和为0的情况,这种情况不能插到新表里面去。(详见我的上一篇文章)(8条消息) 西工大NOJ数据结构实验——2.2稀疏矩阵加法,实现C=A+B_没耳朵的Rabbit的博客-CSDN博客
5.最后,提供一个测试用例,兴许对你有帮助(直接复制粘贴到exe黑框里面就行)
3 3 9 9
1 1 1
1 2 2
1 3 3
2 1 4
2 2 5
2 3 6
3 1 7
3 2 8
3 3 9
1 1 9
1 2 8
1 3 7
2 1 6
2 2 5
2 3 4
3 1 3
3 2 2
3 3 11 1 10
1 2 10
1 3 10
2 1 10
2 2 10
2 3 10
3 1 10
3 2 10
3 3 10--------------------------------
Process exited after 13.05 seconds with return value 0
请按任意键继续. . .
最后,还是完整的代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
typedef int ElemType;
typedef struct OLNode{
int i,j;//行号和列号
ElemType elem;
struct OLNode *right;//右边元素
struct OLNode *down;//下边元素
}OLNode,*OLink;
typedef struct CrossList{
OLink* rHead;
OLink* cHead;
int mu,nu,tu;//行数、列数、非零元个数
}CrossList;
CrossList* CreateOLSMatrix(int r,int c,int t){//创建目标十字链表的表头
CrossList* p=(CrossList*)malloc(sizeof(CrossList));
if(p==NULL){
printf("Out of space!");
}
p->mu=r;
p->nu=c;
p->tu=t;
p->rHead=(OLink*)malloc((r+1)*sizeof(OLink));
p->cHead=(OLink*)malloc((c+1)*sizeof(OLink));
if(p->rHead==NULL||p->cHead==NULL) printf("Out of space!\n");
for(int i=0;i<r+1;i++){
p->rHead[i]=NULL;
}//强迫症,初始化操作
for(int i=0;i<c+1;i++){
p->cHead[i]=NULL;
}//强迫症,初始化操作
return p;
}
void InsertMatrix(CrossList* p,int i,int j,int elem){//插入数据
OLink p1=(OLink)malloc(sizeof(OLNode));
if(p1==NULL) printf("Out of space!\n");
p1->i=i;p1->j=j;p1->elem=elem;
//创建并填充结点
if(p->rHead[i]==NULL||p->rHead[i]->j>j){
p1->right=p->rHead[i];
p->rHead[i]=p1;
}else{
OLink q=p->rHead[i];
while(1){
if(q->right==NULL||q->right->j>j) break;
q=q->right;
}//找到插入位置
p1->right=q->right;
q->right=p1;
}//完成行插入
if(p->cHead[j]==NULL||p->cHead[j]->i>i){
p1->down=p->cHead[j];
p->cHead[j]=p1;
}else{
OLink q=p->cHead[j];
while(1){
if(q->down==NULL||q->down->i>i) break;
q=q->down;
}//找到插入位置
p1->down=q->down;
q->down=p1;
}//完成列插入
}
int BuildMatrix(CrossList* p){//完善表
for(int k=1;k<=p->tu;k++){
int i,j,elem;
scanf("%d%d%d",&i,&j,&elem);
InsertMatrix(p,i,j,elem);
}
}
int OutputMatrix(CrossList* p){//输出表
for(int i=1;i<=p->mu;i++){
OLink q=p->rHead[i];
for(int j=1;j<=p->nu;j++){
if(q!=NULL&&j==q->j){
printf("%d %d %d\n",q->i,q->j,q->elem);
q=q->right;
}
}
}//按照先行后列的顺序输出
}
int AddMatrix(CrossList* p1,CrossList* p2,CrossList* p3){//矩阵相加得到新矩阵p3
for(int i=1;i<=p1->mu;i++){
//先行后列,一点一点地来做
OLink temp1=p1->rHead[i];
OLink temp2=p2->rHead[i];
for(int j=1;j<=p1->nu;j++){
int flag1=temp1!=NULL&&j==temp1->j;
int flag2=temp2!=NULL&&j==temp2->j;
//为什么我要用flag呢?因为都放在if后面的话,if会变的很长很长,超级麻烦
if(flag1==1&&flag2!=1){//如果这个位置上,p1有数据,p2没数据
InsertMatrix(p3,temp1->i,temp1->j,temp1->elem);
temp1=temp1->right;
}
if(flag1!=1&&flag2==1){//如果这个位置上,p1没数据,p2有数据
InsertMatrix(p3,temp2->i,temp2->j,temp2->elem);
temp2=temp2->right;
}
if(flag1==1&&flag2==1){//如果这个位置上,p1有数据,p2也有数据
int total=temp1->elem+temp2->elem;
if(total==0){
p3->tu=p3->tu-2;
//如果p1的数据与p2的数据相加为0,那就不用插入
//别忘了p3中非零元素要减2
}else if(total!=0){
InsertMatrix(p3,temp1->i,temp1->j,total);
p3->tu--;
}//如果相加和不为0,那么把和total插入。
//别忘了p3中非零元素要自减
temp1=temp1->right;
temp2=temp2->right;
}
}
}
}
int main(){
//int r,c,t1;
//scanf("%d%d%d",&r,&c,&t1);
int r,c,t1,t2;
scanf("%d%d%d%d",&r,&c,&t1,&t2);
//输入行数、列数、两个矩阵的非零元个数
CrossList* p1=CreateOLSMatrix(r,c,t1);
CrossList* p2=CreateOLSMatrix(r,c,t2);
CrossList* p3=CreateOLSMatrix(r,c,t1+t2);
//创建三个十字链表的表头
//其中p3表示最终相加得到的十字链表
//p1+p2时,可能会有元素合并,或者合并之后结果为0之类的情况出现,导致p3中非零元素小于t1+t2
//但是方便起见,还是记非零元素个数为t1+t2
//要是真有元素合并,以及合并之后结果为0,那“非零元素-1”这一步到时候再做也不迟
BuildMatrix(p1);
BuildMatrix(p2);
AddMatrix(p1,p2,p3);
OutputMatrix(p3);
return 0;
}
如果对你有帮助的话,就请麻烦点个赞叭
真心希望点赞加关注哦
你们的鼓励是我创作路上的不竭动力文章来源:https://www.toymoban.com/news/detail-425455.html
谢谢大家!文章来源地址https://www.toymoban.com/news/detail-425455.html
到了这里,关于西工大NOJ数据结构理论——013.以十字链表为存储结构实现矩阵相加(严5.27)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!