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

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,运行结果为:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值