矩阵十字链表的储存形式(十字链表的创建与相加)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
int i,j;
int elem;
struct node *right,*down;
}cnode,*clink;
typedef struct
{
clink *rhead,*chead; //这个的确是指针的指针,一维只要一维指针,二维需要指针的指针来定位
int m,n,len;
}clist; //clist 是二维平面的开始点,原点
// 创造十字链表是采用头插法的
/*
行的创造
如果该行的头结点后面为空,直接插在后面
如果该行的头结点不为空,但后面节点的列值更大,新来的节点插在头结点后面
新来的节点的列值跟大,则插在已存在节点的后面
同理,列也是这样判断如何插
*/
void createcrosslist(clist *x,int m,int n,int t)
{
int cnt;
int x1,x2,x3;
cnode* y;
clink z;
int i;
x->m=m;
x->n=n;
x->len=t;
if(!(x->rhead=(clink *)malloc((m+2)*sizeof(clink))))//三重平面理解,之前的链表是二重平面,在函数里,因为->的出现,所以三重变四重,二重变三重
exit(0); //节点指针的指针
if(!(x->chead=(clink *)malloc((n+2)*sizeof(clink))))
exit(0);
for(cnt=0;cnt<=m;cnt++)//这样既处理了可能有0行0列的问题 chead[],rhead[]存的节点cnode的地址
{
x->rhead[cnt]=NULL;
}
for(cnt=0;cnt<=n;cnt++)
{
x->chead[cnt]=NULL;
}
for(i=0;i<t;i++)
{
scanf("%d%d%d",&x1,&x2,&x3);
if(!(y=(cnode *)malloc(sizeof(cnode))))//因为->操作,指针变量和非指针变量都是要让其变成指针的
exit(0);
y->i=x1,y->j=x2,y->elem=x3;
y->right=NULL;y->down=NULL;
if(x->rhead[x1]==NULL||x->rhead[x1]->j>y->j) //创建的时候只要考虑是否为空和rhead->j与y->j比,确定是否插入
{
y->right=x->rhead[x1];
x->rhead[x1]=y;
}
else
{ //这步也是为了保护头结点。
z=x->rhead[x1];//z是被赋值的,所以不必在设为指针变量,这里指指针的指针,也就是要用点的话就指针化,转成用->
while((z->right!=NULL)&&(z->right->j<x2))
z=z->right;
y->right=z->right;
z->right=y;
}
if(x->chead[x2]==NULL||x->chead[x2]->i>y->i)
{
y->down=x->chead[x2];
x->chead[x2]=y;
}
else
{
z=x->chead[x2];
while((z->down!=NULL)&&(z->down->i<x1))
z=z->down;
y->down=z->down;
z->down=y;
}
}
return;
}
clink isexist(clist* a,clink p)
{
int x1,x2;
clink q;
x1=p->i;
x2=p->j;
q=a->rhead[x1];
while(q)
{
if(x2==q->j)//这里当然是从第一个找
return q;
else
q=q->right;
} //return 0就是没有找到
return NULL;
}
void insert(clist* a,clink p) //插入与删除都是要利用头结点的,只是这个头结点没有赋值而已
{
int x1,x2;
clink zz;
clink q;
x1=p->i;
x2=p->j;
q=a->rhead[x1]; //不能该变头结点的数据
zz=(clink)malloc(sizeof(cnode));
zz->i=x1;
zz->j=x2;
zz->elem=p->elem; //插到第一个比他大的数前面,
zz->right=NULL; //没有没数据的头结点,所以还要考虑插头的情况
zz->down=NULL; //头插一个数 get
if(q==NULL||q->j>x2) //这行为空的情况,空就写==空更不会出错
{
zz->right=a->rhead[x1];
a->rhead[x1]=zz; // 因为是头指针的移动,所以不能用p,如果用p的话,a->head存的地址并不会改变
}
else
{
while(q->right)
{
if(q->right->j>x2)
{
zz->right=q->right;
q->right=zz;
break;
}
q=q->right;
}
if(q->right==NULL) //尾接一个数
q->right=zz;
//这里直接,不用判断q->j与zz->elem的大小关系,因为到这步,只有zz->elem大于q->j的情况。
}
q=a->chead[x2]; //关键
if(q==NULL||q->i>x1)
{
zz->down=a->chead[x2];
a->chead[x2]=zz;
}
else
{ //如果只有一个元素,直接跳出while,这是头结点的一个弊端
while(q->down) //进入这个else,第一个一定是小于x1的
{
if(q->down->i>x1)
{
zz->down=q->down;
q->down=zz;
break;
}
q=q->down;
}
if(q->down==NULL) //如果到最后没有比他大的数,则插到最后
q->down=zz;
}
return;
}
void Delete(clist* a,clink p)
{
int x1,x2;
clink q;
x1=p->i;
x2=p->j;
q=a->rhead[x1]; //用q保留头结点 ,
if(q->j==x2)
{
a->rhead[x1]=a->rhead[x1]->right;//删头结点,get.
free(q);
}
else //是从第二个开始
{
while(q->right->j!=x2)
{
q=q->right;
}
q->right=q->right->right;
}
}
void print(clist* a)
{
int k;
clink p;
for(k=0;k<=a->m;k++)
{
p=a->rhead[k];
while(p)
{
printf("%d %d %d\n",p->i,p->j,p->elem);
p=p->right;
}
}
return;
}
void addcrosslist(clist* a,clist* b)//利用链表的插入与删除
//原理:链表的合并
{
int k;
clink p;
clink pp=NULL;
for(k=0;k<=b->m;k++) //b->m是cnt类的,所以是小于号
{
p=b->rhead[k];
while(p) //每行用指针逐个后移
{
pp=isexist(a,p);
if(!pp) //如果不存在。
insert(a,p);
else
{
pp->elem+=p->elem;
if(!pp->elem)
Delete(a,pp);
}
p=p->right;
}
}
return;
}
int main()
{
int p,q;
clist a,b;
scanf("%d%d",&p,&q);
int t1,t2;
scanf("%d%d",&t1,&t2);
createcrosslist(&a,p,q,t1);
createcrosslist(&b,p,q,t2);
addcrosslist(&a,&b);
print(&a);
return 0;
}
/*
测试用例
3 4 3 2
1 1 1
1 3 1
2 2 2
1 2 1
2 2 3
*/