您的位置:首页 > 编程语言 > C语言/C++

稀疏矩阵和广义表 C语言实现

2013-11-29 16:06 459 查看

1、稀疏矩阵概念

稀疏矩阵是矩阵中的一种特殊情况,其非零元素个数远小于零元素个数。

如下图为一个稀疏矩阵



其对应的三元组线性表表示为:

((1,1,3),(1,4,5),(2,3,-2),(3,1,1),(3,3,4),(3,5,6),(5,3,-1))

2、稀疏矩阵的存储结构

稀疏矩阵的存储结构分为顺序存储和链接存储,链接存储又分为带行指针向量的链接存储与十字链接存储。

a.顺序存储

稀疏矩阵顺序存储结构示意图:



其定义如下:(注意:为方便,下面的下标都从1使用)

struct Triple       //三元组结构定义
{
int row, col;   //行号、列号
ElemType val;   //元素值
};
struct SMatrix     //顺序存储类型定义
{
int m, n, t;   //行数、列数、非零元素个数
struct Triple sm[MaxTerms + 1]; //存储三元组的数组,下标从1开始
};


b、带行指针向量的链接存储

稀疏矩阵带行指针向量的链接存储结构示意图



其定义如下:

struct TripleNode  //三元组结点类型定义
{
int row, col;  //行号、列号
ElemType val;  //元素值
struct TripleNode* next; //指向同一行的下一个结点
};
struct LMatrix     //带行指针向量的链接存储类型定义
{
int m, n, t;   //行数、列数、非零元素个数
struct TripleNode* lm[MaxRows + 1]; //存储行单链表的数组指针,下标从1开始
};


c、十字链接存储

稀疏矩阵十字链接存储结构示意图



其定义如下:

struct CrossNode   //结点类型定义
{
int row, col;  //行号, 列号
ElemType val;  //元素值
struct CrossNode *down, *right;  //指向同一列下一个结点的指针,指向同一行下一个结点的指针
};
struct CLMatrix    //十字链接存储类型定义
{
int m, n, t;   //行数、列数、非零元素个数
struct CrossNode* rm[MaxRows + 1];     //存储行单链表的数组指针,下标从1开始
struct CrossNode* cm[MaxColumns + 1];  //存储列单链表的数组指针,下标从1开始
};

下面通过实例展示稀疏矩阵的操作

#include<stdio.h>
#include<stdlib.h>
#define MaxTerms 10 //非零元素的最大值
#define MaxRows 10 //行数最大值
#define MaxColumns 10
typedef int ElemType;

//a、顺序存储
struct Triple //三元组结构定义
{
int row, col; //行号、列号
ElemType val; //元素值
};
struct SMatrix //顺序存储类型定义
{
int m, n, t; //行数、列数、非零元素个数
struct Triple sm[MaxTerms + 1]; //存储三元组的数组,下标从1开始
};

//b、带行指针向量的链接存储
struct TripleNode //三元组结点类型定义 { int row, col; //行号、列号 ElemType val; //元素值 struct TripleNode* next; //指向同一行的下一个结点 }; struct LMatrix //带行指针向量的链接存储类型定义 { int m, n, t; //行数、列数、非零元素个数 struct TripleNode* lm[MaxRows + 1]; //存储行单链表的数组指针,下标从1开始 };

//c、十字链接存储
struct CrossNode //结点类型定义 { int row, col; //行号, 列号 ElemType val; //元素值 struct CrossNode *down, *right; //指向同一列下一个结点的指针,指向同一行下一个结点的指针 }; struct CLMatrix //十字链接存储类型定义 { int m, n, t; //行数、列数、非零元素个数 struct CrossNode* rm[MaxRows + 1]; //存储行单链表的数组指针,下标从1开始 struct CrossNode* cm[MaxColumns + 1]; //存储列单链表的数组指针,下标从1开始 };
//1、稀疏矩阵初始化
void InitMatrix_S(struct SMatrix* M) //对SMatrix类型初始化
{
M->m = 0;
M->n = 0;
M->t = 0;
}
void InitMatrix_L(struct LMatrix* M) //对LMatrix类型初始化
{
int i;
M->m = 0;
M->n = 0;
M->t = 0;
for (i = 1; i <= MaxRows; i++)
M->lm[i] = NULL;
}
void InitMatrix_CL(struct CLMatrix* M) //对CLMatrix类型初始化
{
int i;
M->m = 0;
M->n = 0;
M->t = 0;
for (i = 1; i <= MaxRows; i++)
M->rm[i] = NULL;
for (i = 1; i <= MaxColumns; i++)
M->cm[i] = NULL;
}

//2、稀疏矩阵的建立
void InputMatri_S(struct SMatrix* M, int m, int n) //采用SMatrix存储类型的稀疏矩阵建立
{
int k = 0;
int row, col;
ElemType val;
M->m = m; //行数
M->n = n; //列数
printf("依次输入每个三元组,每个三元组格式为:元素,元素,元素+回车\n并最后输入(0,0,0)作为结束:\n");
scanf("%d,%d,%d", &row, &col, &val);
while (row != 0) //以输入一个特殊的三元组(0,0,0)结束整个输入过程
{
k++;
M->sm[k].row = row;
M->sm[k].col = col;
M->sm[k].val = val;
scanf("%d,%d,%d", &row, &col, &val); //输入下一个三元组
}
M->t = k; //非零元素个数
}
void InputMatri_L(struct LMatrix* M, int m, int n) //采用LMatrix存储类型的稀疏矩阵建立
{
int k = 0;
int row, col;
ElemType val;
M->m = m;
M->n = n;
printf("依次输入每个三元组,每个三元组格式为:元素,元素,元素+回车\n并最后输入(0,0,0)作为结束:\n");
scanf("%d,%d,%d", &row, &col, &val);
while (row != 0) //以输入一个特殊的三元组(0,0,0)结束整个输入过程
{
struct TripleNode* cp, *newp;
k++;
newp = malloc(sizeof(struct TripleNode));
newp->row = row;
newp->col = col;
newp->val = val;
newp->next = NULL;
cp = M->lm[row];
if (cp == NULL) //所在行单链表为空时
M->lm[row] = newp;
else
{
while (cp->next != NULL) //通过循环,指针依次后移,把新节点链接到所在行单链表的末尾
cp = cp->next;
cp->next = newp;
}
scanf("%d,%d,%d", &row, &col, &val);
}
M->t = k; //非零元素个数
}
void InputMatri_CL(struct CLMatrix* M, int m, int n) //采用CLMatrix存储类型的稀疏矩阵建立
{
int k = 0;
int row, col;
ElemType val;
M->m = m;
M->n = n;
printf("依次输入每个三元组,每个三元组格式为:元素,元素,元素+回车\n并最后输入(0,0,0)作为结束:\n");
scanf("%d,%d,%d", &row, &col, &val);
while (row != 0) //以输入一个特殊的三元组(0,0,0)结束整个输入过程
{
struct CrossNode *cp, *newp;
k++;
newp = malloc(sizeof(struct CrossNode));
newp->row = row;
newp->col = col;
newp->val = val;
newp->right = newp->down = NULL;
cp = M->rm[row];
if (cp == NULL) //所在行单链表为空时
M->rm[row] = newp;
else
{
while (cp->right != NULL) //通过循环,指针依次后移,把新节点链接到所在行单链表的末尾
cp = cp->right;
cp->right = newp;
}
cp = M->cm[col];
if (cp == NULL) //所在行单链表为空时
M->cm[col] = newp;
else
{
while (cp->down != NULL) //通过循环,指针依次后移,把新节点链接到所在列单链表的末尾
cp = cp->down;
cp->down = newp;
}
scanf("%d,%d,%d", &row, &col, &val);
}
M->t = k; //非零元素个数
}

//3、稀疏矩阵的输出
void OutputMatrix_S(struct SMatrix* M) //采用SMatrix存储类型的稀疏矩阵输出
{
int i;
printf("("); //线性表的左括号
for (i = 1; i < M->t; i++) //以非零元素个数作为循环量,依次输出三元组,因为最后一个三元组的后面不需要跟一个逗号,
//所以这里把前面的三元组和最后一个三元组分开讨论
printf("(%d,%d,%d),", M->sm[i].row, M->sm[i].col, M->sm[i].val);
if (M->t != 0) //最后一个三元组
printf("(%d,%d,%d)", M->sm[M->t].row, M->sm[M->t].col, M->sm[M->t].val);
printf(")\n"); //线性表的右括号
}
void OutputMatrix_L(struct LMatrix* M) //采用LMatrix存储类型的稀疏矩阵输出
{
int i;
struct TripleNode *p;
printf("(");
for (i = 1; i <= M->m; i++)//以行为循环量,依次输出每行的三元组
{
for (p = M->lm[i]; p != NULL; p = p->next)
{
if ((i == M->m) && (p->next == NULL))//因最后一个三元组无需跟逗号,所以做特殊处理
printf("(%d,%d,%d)", p->row, p->col, p->val);
else
printf("(%d,%d,%d),", p->row, p->col, p->val);
}
}
printf(")\n");
}
void OutputMatrix_CL(struct CLMatrix* M) //采用CLMatrix存储类型的稀疏矩阵输出
{
int i;
struct CrossNode *p;
printf("(");
for (i = 1; i <= M->m; i++)//以行为循环量,依次输出每行的三元组
{
for (p = M->rm[i]; p != NULL; p = p->right)
{
if ((i == M->m) && (p->right == NULL))//因最后一个三元组无需跟逗号,所以做特殊处理
printf("(%d,%d,%d)", p->row, p->col, p->val);
else
printf("(%d,%d,%d),", p->row, p->col, p->val);
}
}
printf(")\n");
}

//4、稀疏矩阵的转置运算(采用顺序存储结构)
struct SMatrix* TransposeMatrix_S(struct SMatrix* M)
{
int m, n, t;
int k, i, col;
struct SMatrix* S; //用S暂存转置后的结果
S = malloc(sizeof(struct SMatrix));
InitMatrix_S(S);
m = M->m;
n = M->n;
t = M->t;
S->m = n; //M的列数作为S的行数CrossNode
S->n = m; //M的行数作为S的列数
S->t = t;
if (t == 0) //若是零矩阵
return S;
k = 1; //用k指示S->sm数组的下标
for (col = 1; col <= n; col++) //以列为循环量,扫描所有三元组,并将对应的行列号互换
{
for (i = 1; i <= t; i++)
{
if (M->sm[i].col == col)
{
S->sm[k].row = col;
S->sm[k].col = M->sm[i].row;
S->sm[k].val = M->sm[i].val;
k++;
}
}
}
return S;
}

//5、稀疏矩阵的加法运算(采用带行指针向量的存储结构)
struct LMatrix * AddMatrix_L(struct LMatrix *M1, struct LMatrix *M2)
{
int k, i;
struct LMatrix * M; //用M暂存求和结果
M = malloc(sizeof(struct LMatrix));
InitMatrix_L(M);
if (M1->m != M2->m || M1->n != M2->n)
{
printf("两矩阵大小不相同,无法相加!\n");
exit(1);
}
M->m = M1->m;
M->n = M1->n;
if (M1->t == 0 && M2->t == 0) //若两矩阵均为零矩阵
return M;
k = 0; //用k记录结果矩阵的结点个数
for (i = 1; i <= M->m; i++)
{
struct TripleNode *p1, *p2, *p;
p1 = M1->lm[i]; //p1指向M1矩阵中第i行链表待比较的结点
p2 = M2->lm[i]; //p2指向M2矩阵中第i行链表待比较的结点
p = M->lm[i]; //p指向M矩阵中第i行链表的结尾结点
while (p1 != NULL && p2 != NULL) //当两结点都不为空时进行比较和求和运算
{
struct TripleNode *newp; //产生新结点
newp = malloc(sizeof(struct TripleNode));
if (p1->col < p2->col) //当此行的p1与p2不在同一列,且p1在前面时,把p1所指结点的值赋值给新结点
{
*newp = *p1;
p1 = p1->next;
}
else if (p1->col > p2->col) //当此行的p1与p2不在同一列,且p2在前面时,把p2所指结点的值赋值给新结点
{
*newp = *p2;
p2 = p2->next;
}
else //当p1,p2具有相同列号时,将结点进行相加处理
{
if (p1->val + p2->val == 0) //若相加结果为0,则不建立这个新结点
{
p1 = p1->next;
p2 = p2->next;
free(newp);
continue;
}
else
{
*newp = *p1; //将p1结点赋值给新结点
newp->val += p2->val; //新结点的值等于p1结点的值加p2结点的值
p1 = p1->next;
p2 = p2->next;
}
}
newp->next = NULL; //把新结点的指针域置空
if (p == NULL) //若此行还没有一个结点
M->lm[i] = newp;
else
p->next = newp;
p = newp; //修改p指针,使之指向新的表尾
k++; //结果矩阵的结点数加1
}//while循环结束
while (p1 != NULL) //当p1不为空,p2为空时,直接将p1作为结果结点
{
struct TripleNode *newp; //产生新结点
newp = malloc(sizeof(struct TripleNode));
*newp = *p1;
newp->next = NULL;//把新结点的指针域置空
if (p == NULL) //若此行还没有一个结点
M->lm[i] = newp;
else
p->next = newp;
p = newp; //修改p指针,使之指向新的表尾
p1 = p1->next; //p1指向下一个结点
k++; //结果矩阵的结点数加1
}//while循环结束
while (p2 != NULL) //当p2不为空,p1为空时,直接将p2作为结果结点(实现方法与上面的一样)
{
struct TripleNode *newp; //产生新结点
newp = malloc(sizeof(struct TripleNode));
*newp = *p2;
newp->next = NULL;//把新结点的指针域置空
if (p == NULL) //若此行还没有一个结点
M->lm[i] = newp;
else
p->next = newp;
p = newp; //修改p指针,使之指向新的表尾
p2 = p2->next; //p1指向下一个结点
k++; //结果矩阵的结点数加1
}//while循环结束
}//for循环结束
M->t = k; //把最终的k值作为结果矩阵的结点数
return M; //返回结果矩阵
}

//主函数
void main() //采用带行指针向量的链接存储方式
{
struct LMatrix s1, s2;
struct LMatrix *t;
InitMatrix_L(&s1);
InputMatri_L(&s1, 5, 6);
printf("稀疏矩阵s1的三元组线性表表示为:\n");
OutputMatrix_L(&s1);
InitMatrix_L(&s2);
InputMatri_L(&s2, 5, 6);
printf("稀疏矩阵s2的三元组线性表表示为:\n");
OutputMatrix_L(&s2);
t = AddMatrix_L(&s1, &s2); //求和
printf("s1与s2求和结果的三元组线性表表示为:\n");
OutputMatrix_L(t); //输出求和结果
}



运行结果



3、广义表概念

广义表简称表,它是线性表的推广。

广义表的定义是递归的,其元素可以是某一确定类型的对象(单元素),也可以是由单元素构成的表(子表或表元素)。

广义表可定义如下:

A =()

B = (e)

C=(a,(b,c,d))

D=(A,B,C)=((),(e),(a,(b,c,d)))

E=((a,(a,b)),((a,b),c)))

上面的广义表的图形表示(其中圆圈表示表,方框表示单元素



对于一个非空的广义表,它的第一个元素成为称为该广义表的表头,剩余的所有元素构成的表称为该广义表的表尾:

A: 没有表头和表尾

B: 表头:e;表尾:空表()

C: 表头:a;表尾:表元素((b,c,d))

D: 表头:A(即一个空表);表尾:表元素(B,C),它还可以再分解

E: 表头:表元素(a,(a,b),(a,b),c));表尾:空表()

深度:一个广义表的深度是指该表中括号嵌套的最大次数

          A:1

          B:1

          C:2

          D:3

          E:4

4、广义表存储结构和操作运算

广义表是一种递归的数据结构,很难为每个广义表分配固定大小的存储空间,所以其存储结构只好采用动态链接存储。

上面的广义表的链接存储结构示意图:(每个广义表的结点第一位为标志域,取0表示单元素结点,取1表示子表结点



下面通过实例详细展示广义表的操作和运算

#include<stdio.h>
#include<stdlib.h>
typedef char ElemType; //ElemType类型定义

struct GNode //广义表结点类型定义
{
int tag;   //标志域
union
{
ElemType data;          //值域
struct GNode* sublist;  //子表的表头指针域
};
struct GNode* next;      //指向后继结点的指针域
};

//1、建立广义表
//假定广义表的元素类型ElemType为字符类型char,每个单元素的值被限定为英文字母,
//输入格式为: (a,(#),b,(d,(e)));
//整个表最后以分号作为结束符,#表示空表
//建立广义表需要进行子表递归和后继表递归
void CreateGList_char(struct GNode** GL)
{
char ch;
scanf("%c", &ch);
if (ch == '#') //若输入为空格,则置表头指针为空
*GL = NULL;
else if (ch == '(') //若输入为左括号则建立由*GL所指向的子表结点并递归构造子表
{
*GL = malloc(sizeof(struct GNode));
(*GL)->tag = 1;
CreateGList_char(&((*GL)->sublist)); //递归
}
else //若输入为字符则建立由*GL所指向的单元素结点
{
*GL = malloc(sizeof(struct GNode));
(*GL)->tag = 0;
(*GL)->data = ch;
}
scanf("%c", &ch); //此处读入的字符必为逗号、括号或分号
if (*GL == NULL); //若*GL为空,什么都不做
else if (ch == ',')//若输入为逗号则递归构造后续表
CreateGList_char(&((*GL)->next)); //递归
else if (ch == ')' || ch == ';') //若输入为右括号或分号则置*GL的后继指针域为空
(*GL)->next = NULL;
}

//2、打印输出广义表
//打印输出上面的广义表同样需要递归子表和递归后继表
void PrintfGList_char(struct GNode* GL)
{
if (GL->tag == 1) //表结点的处理情况
{
printf("%c", '('); //先输出左括号,作为开始符号
if (GL->sublist == NULL) //若子表为空
printf("%c", '#');
else
PrintfGList_char(GL->sublist); //递归
printf("%c", ')'); //当一个子表输出结束后,输出一个右括号终止符
}
else //单元素结点的处理情况
printf("%c", GL->data);
if (GL->next != NULL) //若结点还有后继表,则递归输出
{
printf("%c", ','); //先输出逗号分隔符
PrintfGList_char(GL->next); //递归
}
}

//3、求广义表的长度
//广义表中,同一层次的每个结点是通过next域链接起来的,所以可把它看做是由next域链接起来的单链表。
//这样求广义表的长度就是求单链表的长度
//如单链表非空,其长度等于1加其后继单链表的长度,若单链表为空,则长度为0,这是递归的终止条件
int LenthGList(struct GNode* GL)
{
if (GL != NULL)
return 1 + LenthGList(GL->next);
else
return 0;
}

//4、求广义表的深度
//广义表深度的递归定义是它等于所有子表中的最大深度加1,若一个表为空或仅由单元素组成,则深度为1
int DepthGList(struct GNode* GL)
{
int max = 0; //所有子表中最大深度,初始值为0
while (GL != NULL)
{
if (GL->tag == 1)
{
int dep = DepthGList(GL->sublist); //递归调用求出一个子表的深度,用dep表示任一子表的深度
if (dep > max)
max = dep; //让max始终为同一层所求过的子表中深度的最大值
}
GL = GL->next;     //使GL指向同一层的下一个结点
}
return max + 1; //返回表的深度
}

//主函数
void main()
{
struct GNode* gl;
printf("输入一个广义表,输入格式为: (a,(#),b,(d,(e))); \n");
CreateGList_char(&gl);
printf("输出广义表:\n");
PrintfGList_char(gl);
printf("\n");
printf("广义表的长度为:");
printf("%d\n", LenthGList(gl->sublist));
printf("广义表的深度为:");
printf("%d\n", DepthGList(gl->sublist));
}


分别对上面的广义表D与广义表E进行测试

对广义表D ,运行结果为:



对广义表E,运行结果为:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  稀疏矩阵 广义表