稀疏矩阵的十字链表存储表示

/*

Name: 稀疏矩阵的十字链表存储表示

Copyright:

Author: 巧若拙

Date: 29-10-14 21:25

Description:

//-------------------------------------------------------------------------

除了用三元组顺序表来存储压缩矩阵,我们还可以用链表结构来存储,实际上后者应用更广泛,

因为当非零元素的数目较大时,三元组的时间复杂度实在太高。链表结构中最常见的是十字链表,

在十字链表中,稀疏矩阵每一行用一个带头结点的循环链表表示,每一列也用一个带头结点的循环链表表示。

在这个结构中,除头结点外,每个结点都代表矩阵中的一个非零元素,

它由5个域组成:行域(row),列域(col),数据域(data),向下域(down)和向右域(right)。

为了使所有结点的存储结构一致,规定表头结点的结构与非零元素的结点完全一致,只是将其行域和列域设置为0,

由于每一行链表的表头(行头)和每一列链表的表头(列头)的行域和列域的值均为0,故这两组表头结点可以共用,

即同行号,同列号的行头和列头共同存储在一个结点中,只是将其逻辑分开(实际上行头使用该结点的向右域,

而列头使用该结点的向下域),起到资源共享的效果。由此可知,稀疏矩阵的十字链表存储表示的结点总数等于非零元素的个数加上行头和列头

(两者共用标号相同的结点,实际上只要提供其中的较大者的个数就可以了),再加上总表头Head[0],

其中总表头Head[0]的行域和列域分别存储矩阵的总行数和总列数。

因为数组Head[]不是全局变量,且定义Head[]的函数返回的是Head[0],故需要把Head[]的各个元素做成循环链表,

用指针tag.next把各表头结点按顺序连接起来,否则函数调用完毕会丢失数据。

*/

//------稀疏矩阵的十字链表存储表示-------------------

#include <stdio.h>

#include <stdlib.h>

#define N 10

#define MAXRC 100 //假设矩阵的行(列)数最多为100

typedef int ElemType;

typedef struct matnode

{

int row, col;  //结点的行域和列域

struct matnode *right, *down;//结点的向下域(down)和向右域(right)

union //结点的数据域,若为表头结点则无数值,而用指向其后继的指针代替

{

ElemType data;

struct matnode *next;

} tag;

} CrossNode, *CrossList;

void CreateHead(CrossList Head[], int len); //创建十字链表的表头结点

void Insert(CrossList Head[], int r, int c, ElemType v);//插入新的结点

CrossList CreateSMatrix_OL(int m, int n); //创建一个十字链表

void PrintCrossList(CrossList T);//输出十字链表存储矩阵

void PrintArray(int H[][N], int m, int n); //输出二维数组存储矩阵

CrossList ArrayToCrossList(int A[][N], int m, int n);//二维数组转换为十字链表

void CrossListToArray(CrossList T, int A[][N], int *m, int *n);//十字链表转换为二维数组

CrossList TransposeCrossList(const CrossList M);  //十字链表存储转置矩阵

CrossList AddSMatrix(const CrossList A, const CrossList B);//十字链表存储矩阵矩阵相加

CrossList MultCrossList(const CrossList A, const CrossList B);//十字链表存储矩阵矩阵相乘

int main(void)

{

int m, n;

ElemType A[N][N], B[N][N]; //存储矩阵的二维数组

CrossList  CL1=NULL, CL2=NULL, CL3=NULL, CL4=NULL, CL5=NULL; // 十字链表

m = 3;

n = 2;

CL1 = CreateSMatrix_OL(m, n); //创建一个十字链表

PrintCrossList(CL1);//输出十字链表存储矩阵

CrossListToArray(CL1, A, &m, &n);//十字链表转换为二维数组

PrintArray(A, m, n); //输出二维数组存储矩阵

CL2 = ArrayToCrossList(A, m, n); //二维数组转换为十字链表

PrintCrossList(CL2);//输出十字链表存储矩阵

CL3 = TransposeCrossList(CL2);  //十字链表存储转置矩阵

PrintCrossList(CL3);//输出十字链表存储矩阵

CrossListToArray(CL3, B, &m, &n);//十字链表转换为二维数组

PrintArray(B, m, n); //输出二维数组存储矩阵

CL4 = AddSMatrix(CL1, CL2);//十字链表存储矩阵矩阵相加

CrossListToArray(CL4, B, &m, &n);//十字链表转换为二维数组

PrintArray(B, m, n); //输出二维数组存储矩阵

CL5 = MultCrossList(CL2, CL3);//十字链表存储矩阵矩阵相乘

CrossListToArray(CL5, B, &m, &n);//十字链表转换为二维数组

PrintArray(B, m, n); //输出二维数组存储矩阵

system("pause");

return 0;

}

//---------------------------------------------------------------------------------

//创建一个十字链表

void CreateHead(CrossList Head[], int len) //创建十字链表的表头结点

{

CrossList p;

int i;

p = (CrossList)malloc(sizeof(CrossNode));

if (!p)

{

puts("Error");

exit(1);

}

Head[0] = p;  //生成总表头结点

for (i=1; i<=len; i++)//先初始化所有的行头(列头)

{

p = (CrossList)malloc(sizeof(CrossNode));

if (!p)

{

puts("Error");

exit(1);

}

Head[i] = p;

Head[i-1]->tag.next = p;//因为数组Head[]不是全局变量,需要把表头结点按顺序连接起来,否则函数调用完毕会丢失数据

p->row = p->col = 0; //表头结点的行域和列域设置为0

p->down = p->right = p;//先将表头结点的行域和列域指向自身,以构成循环链表

}

Head[len]->tag.next = Head[0];//最后一个表头结点的后继为总表头,即构成循环链表

}

void Insert(CrossList Head[], int r, int c, ElemType v) //插入新的结点

{

CrossList p, q;

p = (CrossList)malloc(sizeof(CrossNode));

if (!p)

{

puts("Error");

exit(1);

}

p->row = r;

p->col = c;

p->tag.data = v;

q = Head[r];   //完成行插入

while (q->right != Head[r] && q->right->col < c)

q = q->right;

p->right = q->right;

q->right = p;

q = Head[c]; //完成列插入

while (q->down != Head[c] && q->down->row < r)

q = q->down;

p->down = q->down;

q->down = p;

}

CrossList CreateSMatrix_OL(int m, int n) //创建一个十字链表

{

int s; //头结点个数s

int r, c;//元素的行号r,列号c

ElemType v;//元素的值v

CrossList Head[MAXRC], p, q;

s = m > n ? m : n;//因为序号相同行头和列头共用一个结点,故只要分配s个表头结点就好了

CreateHead(Head, s);//创建十字链表的表头结点

puts("请按行序为主序依次输入矩阵的非零元素的行号,列号和元素值,每行输入一个元素的信息:");

printf("注意行号不能超过%d,列号不能超过%d,否则结束输入\n", m, n); //生成结点

do {

scanf("%d%d%d", &r, &c, &v);

fflush(stdin);

if (r > 0 && r <= m && c > 0 && c <= n)

{

Insert(Head, r, c, v);

}

} while (r > 0 && r <= m && c > 0 && c <= n) ;

Head[0]->row = m; //总表头Head[0]的行域存储矩阵的总行数

Head[0]->col = n; //总表头Head[0]的列域存储矩阵的总列数

return Head[0]; //返回总表头结点

}

void PrintCrossList(CrossList T)//输出十字链表存储矩阵

{

int k = 0;

CrossList p, q; //p指向行头,q指向非零元素结点

if (T == NULL)

{

printf("矩阵为空\n");

}

printf("\n");

p = T->tag.next;

while (p != T)

{

q = p->right;

while(q != p)

{

printf("T%d (%d,%d,%d)\t", ++k, q->row, q->col, q->tag.data);

q = q->right;

}

p = p->tag.next;

}

printf("\n");

}

void PrintArray(int H[][N], int m, int n) //输出二维数组存储矩阵

{

int i, j;

for (i=0; i<m; i++)

{

for (j=0; j<n; j++)

printf("%4d ", H[i][j]);

printf("\n");

}

}

//二维数组存储稀疏矩阵和十字链表的相互转换

CrossList ArrayToCrossList(int A[][N], int m, int n)//二维数组转换为十字链表

{

int i, j, s;

CrossList Head[MAXRC], p, q;

s = m > n ? m : n;//因为序号相同行头和列头共用一个结点,故只要分配s个表头结点就好了

CreateHead(Head, s);

for (i=0; i<m; i++)

for (j=0; j<n; j++)

if (A[i][j] != 0)

{

Insert(Head, i+1, j+1, A[i][j]);

}

Head[0]->row = m; //总表头Head[0]的行域存储矩阵的总行数

Head[0]->col = n; //总表头Head[0]的列域存储矩阵的总列数

return Head[0]; //返回总表头结点

}

void CrossListToArray(CrossList T, int A[][N], int *m, int *n)//十字链表转换为二维数组

{

int i, j;

CrossList p, q; //p指向行头,q指向非零元素结点

p = T->tag.next;

if (p != T) //如果非零元素个数不为零,存储其总行数和总列数

{

*m = T->row;

*n = T->col;

}

for (i=0; i<*m; i++) //先设所有元素均为0

for (j=0; j<*n; j++)

A[i][j] = 0;

while (p != T)  //按行表顺序,把十字链表中的元素写入二维数组

{

q = p->right;

while (q != p) //注意二维数组的下标从0开始,而十字链表的行,列号从1开始

{

A[q->row-1][q->col-1] = q->tag.data;

q = q->right;

}

p = p->tag.next;

}

}

//-------------------------------------------------------------------------

//1。转置矩阵

//  算法很简单,只要使新矩阵与原矩阵的行列值互换就好了。我们以行序为主序进行转置。

//  其函数如下;

CrossList TransposeCrossList(const CrossList M)  //十字链表存储转置矩阵

{

int i, s;

CrossList Head[MAXRC], p, q;

s = M->row > M->col ? M->row : M->col;//因为序号相同行头和列头共用一个结点,故只要分配s个表头结点就好了

CreateHead(Head, s);

p = M->tag.next;

while (p != M)  //按行表顺序,进行转置

{

q = p->right;

while (q != p)

{

Insert(Head, q->col, q->row, q->tag.data);

q = q->right;

}

p = p->tag.next;

}

Head[0]->row = M->col;

Head[0]->col = M->row;

return Head[0];

}

//2。矩阵相加

//基本算法,依次扫描A和B的行列值,并且以行序为主序。当行列相同时,将两个元素值相加产生

//的结果插入结果链表中;不相同时,将A或B的结点直接插入结果链表中。

// 这种算法的时间复杂度为O(A.tu+B.tu),速度非常快。其函数如下;

CrossList AddSMatrix(const CrossList A, const CrossList B)//十字链表存储矩阵矩阵相加

{

int i, s;

CrossList Head[MAXRC], pa, pb, qa, qb;

if (A->row != B->row || A->col != B->col)

{

puts("两个矩阵不是同类型的,不能相加");

exit(1);

}

else

{

s = A->row > A->col ? A->row : A->col;//因为序号相同行头和列头共用一个结点,故只要分配s个表头结点就好了

CreateHead(Head, s);

pa = A->tag.next;

pb = B->tag.next;

while (pa != A) //按行序处理

{

qa = pa->right;

qb = pb->right;

while (qa != pa && qb != pb)//从左到右处理该行非零结点,直到A,B 中有一个结束

{

if (qa->col < qb->col) //A的列号小于B的列号,将A的结点直接放入C中

{

Insert(Head, qa->row, qa->col, qa->tag.data);

qa = qa->right;

}

else if (qa->col > qb->col)//B的列小于A的列,将B的结点直接放入C中

{

Insert(Head, qb->row, qb->col, qb->tag.data);

qb = qb->right;

}

else if (qa->tag.data + qb->tag.data != 0) //列号相等 ,当结果值不为0时放入C中

{

Insert(Head, qa->row, qa->col, qa->tag.data + qb->tag.data);

qa = qa->right;

qb = qb->right;

}

}

if (qa == pa) //该行A结束,若B还有元素,则将B的元素直接放入C中

{

while (qb != pb)

{

Insert(Head, qb->row, qb->col, qb->tag.data);

qb = qb->right;

}

}

else //该行B结束,若A还有元素,则将A的元素直接放入C中

{

while (qa != pa)

{

Insert(Head, qa->row, qa->col, qa->tag.data);

qa = qa->right;

}

}

pa = pa->tag.next;

pb = pb->tag.next;

}

}

Head[0]->row = A[0].row; //总表头Head[0]的行域存储矩阵的总行数

Head[0]->col = A[0].col; //总表头Head[0]的列域存储矩阵的总列数

return Head[0]; //返回总表头结点

}

//

//3。矩阵相乘

// 由于C中元素的行号和A中元素的行号一致,又都是以行序为主序排列的,因此可以对A(即对C)

//进行逐行处理。对A中第i行的每一个结点,我们在B中寻找相对应的结点,即A中第k列对应B中第k行。

// 因为C中元素的列号和B中元素的列号一致,所以每处理一行C结点,就要对B的每一列进行处理,

//根据矩阵乘法的特点,对每个C元素设置一个累计乘积值的变量,使其初值为0,每处理一行A,

//都要按列序扫描B一次,求得相应元素的乘积值并累积到相应的累计乘积值的变量中。

// 这种算法的时间复杂度为O(A.tu*B.mu)或O(A.nu*B.tu),速度非常快。其函数如下;

CrossList MultCrossList(const CrossList A, const CrossList B)//十字链表存储矩阵矩阵相乘

{

int s, i;

ElemType value; //累积乘积值,作为C在对应的行列号处的结点的值

CrossList Head[MAXRC], pa, pb, qa, qb;

if (A->col != B->row)

return NULL;

s = A->row > B->col ? A->row : B->col;//因为序号相同行头和列头共用一个结点,故只要分配s个表头结点就好了

CreateHead(Head, s);

pa = A->tag.next;

while (pa != A) //按行序处理

{

pb = B->tag.next;

while (pb != B) //按列序处理

{

qa = pa->right;

qb = pb->down;

value = 0;

while (qa != pa && qb != pb)//A从左到右处理该行非零结点,B从上到下处理该列非零结点,

{                      //直到A,B 中有一个结束

if (qa->col < qb->row) //如果qa的列号小于qb的行号,qa右移一位

qa = qa->right;

else if (qa->col > qb->row) //如果qa的列号大于qb的行号,qb下移一位

qb = qb->down;

else      //如果qa的列号等于qb的行号,二者相乘,乘积值累积在value中,

{

value += qa->tag.data * qb->tag.data;

qa = qa->right;

qb = qb->down;

}

}

if (value != 0) //如果value不为0,把它放入C中

{

Insert(Head, pa->right->row, pb->down->col, value);

}

pb = pb->tag.next;

}

pa = pa->tag.next;

}

Head[0]->row = A[0].row; //总表头Head[0]的行域存储矩阵的总行数

Head[0]->col = B[0].col; //总表头Head[0]的列域存储矩阵的总列数

return Head[0];

}

时间: 2024-10-23 09:38:11

稀疏矩阵的十字链表存储表示的相关文章

javascript实现数据结构:稀疏矩阵的十字链表存储表示

当矩阵的非零个数和位置在操作过程中变化大时,就不宜采用顺序存储结构来表示三元组的线性表.例如,在作"将矩阵B加到矩阵A上"的操作时,由于非零元的插入或删除将会引起A.data中元素的移动.为此,对这种类型的矩阵,采用链式存储结构表示三元组的线性表更为恰当. 在链表中,每个非陵园可用一个含5个域的结点表示,其中i,j和e这3个域分别表示该非零元所在的行,列和非零元的值,向右域right用以链接同一行中下一个非零元,向下域down用以链接同一列中下一个非零元.同一行的非零元通过right域

数据结构之---C/C++实现稀疏矩阵的十字链表

首先这里介绍什么是矩阵的十字链表,大家可以理解稀疏矩阵是顺序存储的,那么这个就是链式存储的. 如图: 存储该矩阵 那么应该是如下的格式: 我们知道稀疏矩阵的三元组存储方式的实现很简单,每个元素有三个域分别是col,row, e.代表了该非零元的行号.列号以及值.那么在十字链表的存储方式下,首先这三个域是肯定少不了的,不然在进行很多操作的时候都要自己使用计数器,很麻烦.而十字链表的形式大家可以理解成每一行是一个链表,而每一列又是一个链表 通过上面的图我们可以知道,每个结点不止要存放row, col

数据结构之---C++语言实现图的十字链表存储表示

最近一直忙着考研复习,很久都没有更新博客了,今天写一篇数据结构的存储. //有向图的十字链表存储表示 //杨鑫 #include <iostream> #include <cstdio> #include <stdlib.h> #include <cstring> using namespace std; #define MAX_VERTEX_NUM 20 #define OVERFLOW -2 #define OK 1 typedef int Status

7-3-有向图的十字链表存储结构-图-第7章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第7章  图 - 有向图的十字链表存储结构 ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? Status.h.Scanf.c.LinkQueue.c        

有向图的十字链表存储形式

十字链表是有向图的另一种链式存储结构.可以看成是将有向图的邻接表和逆邻接表(只考虑入度)结合起来得到的一种链表.在十字链表中,对应于有向图中每一个顶点有一个节点,每一条弧也有一个结点. 顶点之间是数组顺序存储,而弧是链式存储. 弧结点结构: 顶点结点结构: 十字链表形态: 实现: /*********************************************** 有向图的存储形式--十字链表 by Rowandjj 2014/6/27 ***********************

看数据结构写代码(21) 稀疏矩阵(十字链表方式)

写完 这个样例,花费了 我不少时间.大部分时间 花费在 调试 内存问题上. 比如在销毁十字链表时.多次释放节点空间,造成 _CrtIsValidHeapPointer(pUserData) 异常. 当使用malloc 分配 一个 空间时,会将这个空间的起始地址和长度 加到一个链表中去.free(p)的时候 ,会从 链表里 查找 是否 有 这个地址空间,找到了就将这个节点从链表中删除._CrtIsValidHeapPointer(pUserData)  这个函数 正是 检查 这个空间是否 在链表里

_DataStructure_C_Impl:稀疏矩阵十字链表存储

#include<stdio.h> #include<stdlib.h> typedef int DataType; typedef struct OLNode{ int i,j; DataType e; struct OLNode *right,*down; }OLNode,*OLink; typedef struct{ OLink *rowhead,*colhead; int m,n,len; }CrossList; //初始化稀疏矩阵 void InitMatrix(Cros

图的十字链表存储(C语言)

时间一晃已经大二下了,学校也开了数据结构的课,想起了自己大一刚会C语言,自学数据结构的时候,那时候很无助啊,不懂就只有拼命看,改bug改很久. 老师一节课讲完了邻接表,十字链表,邻接多重表.然而感觉他好像在自己讲自己的,一点也不认真. 但是依托老师是不行的,只懂理论也不行,或许学生认为邻接矩阵,邻接表不就那种东西吗?很简单啊. 大一的时候我也觉得C语言很简单啊,然后一写不都是错? 不写代码的数据结构不叫数据结构. 不多说,开始吧. 邻接表固然优秀,但也有不足,例如对有向图的处理上,有时候需要再建

基于十字链表的两个稀疏矩阵相乘

#include <stdio.h> #include <stdlib.h> #include <string.h> typedef int DataType;// 稀疏矩阵的十字链表存储表示 typedef struct LNode { int i,j; // 该非零元的行和列下标 DataType e; // 非零元素值 struct LNode *right,*down; // 该非零元所在行表和列表的后继链域 }LNode, *Link; typedef str