//【数据结构】NOJ013 以十字链表为存储结构实现矩阵相加
#include <stdio.h>
#include <stdlib.h>
//十字链表
typedef int ElemType; //元素类型
typedef struct LNode{
int Row,Col; //行数、列数
ElemType Elem; //元素
struct LNode *Right,*Down; //右侧、下侧
}LNode,*PLNode;
typedef struct CrossList{
int mu,nu,tu; //总行数、总列数、总个数
PLNode *rHead,*cHead; //存放指向首结点的指针的数组
}*CList;
CList Init(int m,int n,int t); //初始化并插入初始元素
void Add(CList L1,CList L2); //矩阵相加存入L1
int Insert(CList L, PLNode N); //插入一个元素
void Delete(CList L,PLNode p); //删除一个元素
void Output(CList L); //按行输出
CList Init(int m,int n,int t)
{//初始化,插入初始元素
//初始化
CList L=(CList)malloc(sizeof(struct CrossList));
L->mu=m; L->nu=n; L->tu=t;
L->rHead=(PLNode*)malloc((m+1)*sizeof(LNode));
L->cHead=(PLNode*)malloc((n+1)*sizeof(LNode));
int i;
for(i=0;i<m;i++) L->rHead[i+1]=NULL;
for(i=0;i<n;i++) L->cHead[i+1]=NULL;
//插入t个元素
for(i=0;i<t;i++){
//新建结点
PLNode N=(PLNode)malloc(sizeof(struct LNode));
N->Right=N->Down=NULL;
scanf("%d %d %d",&N->Row,&N->Col,&N->Elem);
//插入结点
Insert(L,N);
}
return L;
}//Init
void Add(CList L1,CList L2)
{//相加结果存在L1中
if(!L1->tu) L1=L2; //L1为空
else if(L2->tu){ //L1、L2均不为空
PLNode P; //遍历L2中结点
int i;
for(i=1;i<L2->mu+1;i++){
P=L2->rHead[i];
while(P){
PLNode Q=(PLNode)malloc(sizeof(struct LNode));
Q->Col=P->Col;
Q->Row=P->Row;
Q->Elem=P->Elem;
Q->Down=Q->Right=NULL;
Insert(L1,Q);
P=P->Right;
}
}
}
}//Add
int Insert(CList L,PLNode N)
{//插入一个元素,若L中已有就将元素值相加,若相加为0就删除
int col=N->Col,row=N->Row;
PLNode q; //结点N应插入到q之后
//查找相同位置的元素
for(q=L->rHead[row];(q)&&((q->Col) < N->Col);q=q->Right)
;
if(q&&(q->Col==N->Col)){ //将对应元素相加
q->Elem+=N->Elem;
if(q->Elem==0)Delete(L,q); //如果相加为0,则删除该结点
return 0;
}
//若L中没有相同位置的元素则插入该元素
//插入行中
if(!L->rHead[row]||((L->rHead[row]->Col)>N->Col)){ //此行为空或首节点的列数大于待插入结点列数
N->Right=L->rHead[row];L->rHead[row]=N; //作为首节点插入该行
}
else{ //寻找待插入位置q
for(q=L->rHead[row];(q->Right)&&((q->Right->Col) < N->Col);q=q->Right)
;
N->Right=q->Right;q->Right=N; //将N插入到q之后
}//行插入完成
//插入列中
if(!L->cHead[col]||((L->cHead[col]->Row)> N->Row)){ //此列为空或首节点的行数大于待插入结点行数
N->Down=L->cHead[col];L->cHead[col]=N; //作为首节点插入该列
}
else{ //寻找待插入位置q
for(q=L->cHead[col];(q->Down)&&((q->Down->Row) < N->Row);q=q->Down)
;
N->Down=q->Down;q->Down=N; //将N插入到q之下
}//列插入完成
return 0;
}//Insert
void Delete(CList L,PLNode p)
{
PLNode Del=p; //记录待删除结点位置
PLNode Tmp=L->rHead[Del->Row];
if(Tmp==Del)L->rHead[Del->Row]=Del->Right; //待删除结点为首节点
else{
while(Tmp->Right!=Del)Tmp=Tmp->Right; //寻找该结点在行中位置
Tmp->Right=Del->Right; //从行中删除
}
Tmp=L->cHead[Del->Col];
if(Tmp==Del)L->cHead[Del->Col]=Del->Down; //待删除结点为首节点
else{
while(Tmp->Down!=Del)Tmp=Tmp->Down; //寻找该结点在列中位置
Tmp->Down=Del->Down; //从列中删除
}
free(Del); //终于删除掉了。。。
}//Delete
void Output(CList L)
{//按行输出
if(L->tu){
int i;
PLNode P;
for(i=1;i<L->mu+1;i++)
for(P=L->rHead[i];P;P=P->Right)
printf("%d %d %d\n",P->Row,P->Col,P->Elem);
}
else printf("Empty.");
}//Output
int main()
{
int m,n,t1,t2;
scanf("%d %d %d %d",&m,&n,&t1,&t2);
CList L1=Init(m,n,t1);
CList L2=Init(m,n,t2);
//Insert(L1,1,1,-1);
Add(L1,L2);
Output(L1);
//Output(L2);
return 0;
}
【后记】
1.这段裹脚布代码写了将近六个小时才ac,期间Add函数“遍历L2中元素结点”的写法还偷窥了薛学长的博客才有思路。
2.最开始写的时候,是想把Insert函数的参数设为“待插入结点的行数、列数、元素值”,打算把Init和Insert统一起来,然而没成功,遂将其参数改为“待插入结点”
3.Delete函数最开始没有考虑到删除首节点的情况
4.昨晚写的时候测试了一些数据是对的,然而还是WA,以为是换行问题调了好久(也没调成功),结果发现不是这个问题
5.感觉自己不检测分配内存成功与否总有一天是要出事儿的,然而……
6.遗憾是Insert函数需要区别“已有元素”的情况,Delete函数需要从头遍历删除位置,看起来很难受……
7.出现的各种bug:没有初始化结点的指针Right、Down为NULL、插入位置的if-else分类条件搞不清……
8.ac了好开心哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈