数据结构c语言版上海交通大学出版社项目三《稀疏矩阵相加》

两个稀疏矩阵 A和 B采用十字链表方式存储,计算 C=A+B,C也采用十字链表方式存储。

根据矩阵相加的运算法则,若将矩阵 B加到矩阵A上,对于A的十字链表来说,可能进行的操作有:

① 当 aij 与 bij 均不等于 0,且 aij+bij≠0 时,改变结点的 value 值;

② 当 aij≠0 且 bij=0 时,value 值不变;

③ 当 aij=0且 bij≠0 时,插入一个新结点;

④ 当 aij与 bij 均不等于 0,且 aij+bij=0时,删除一个结点。

进行运算时,从矩阵的第一行开始逐行进行比较,对每一行都从行表头(即A和B在该行中的第一个非零元素结点)出发,假设pa和 pb 分别指向A和 B的十字链表中行值相同的两个结点,该算法基本思想如下:

(1)令pa和 pb 分别指向 A和 B的本行的第一个非零元素的结点。

(2)对本行所有非零结点按以下4种情况进行处理:

① 若 pa->colcol且 pa!=NULL(不是表头结点),则令 pa指向本行下一个非零元素结点;

②) 若 pa->col>pb->col 或 pa=NULL,则在 A 的十字链表中插入一个新结点,且值为pb 所指结点的 value 值,同时 A的列链表中的指针也要作相应改变;

③ 若 pa->col=pb->col且 pa->v+pb->v≠0,则将 B中当前结点的值与 A 中当前结点的值相加后送给 pa所指结点的 value 值;

④ 若 pa->col=pb->col且 pa->v+pb->v=0,则在 A的十字链表中删除 pa 所指的结点同时 A的列链表中的指针也要作相应改变。

重复以上步骤,直到 B的本行中无非零元素结点结束。

(3)令 pa和 pb 指向下一行的第一个非零元素结点,重复步骤(2)。

另外,为了便于插入和删除结点,需要设置两个指针qa和q,分别指向该结点在同一行中的前驱结点和在同一列中的前驱结点。

代码如下

# include 
# include 
# define MAX 100

// 定义稀疏矩阵的节点结构
typedef struct OLNode {
    int row, col; // 行号和列号
    int v; // 值
    struct OLNode *right, *down; // 指向右边和下边的节点指针
} OLNode, *OLink;

// 定义稀疏矩阵的十字链表结构
typedef struct {
    OLNode *rhead[MAX], *chead[MAX]; // 行和列的头指针数组
    int m, n, t; // 矩阵的行数、列数和非零元素个数
} CrossList;

// 创建稀疏矩阵的十字链表表示
void CreateCrossList(CrossList *M) {
    int m, n, t, i, r, c, v;
    OLNode *p, *q;
    printf("输入矩阵行数、列数和非零元素个数:");
    scanf("%d%d%d", &m, &n, &t);
    M->m = m;
    M->n = n;
    M->t = t;

    // 初始化行和列的头指针数组
    for (i = 1; i <= m; i++)
        M->rhead[i] = NULL;
    for (i = 1; i <= n; i++)
        M->chead[i] = NULL;

    // 输入非零元素并插入到十字链表中
    for (i = 1; i <= t; i++) {
        printf("输入第%d个非零元素的行号、列号和值:", i);
        scanf("%d%d%d", &r, &c, &v);
        p = (OLNode *)malloc(sizeof(OLNode));
        p->row = r;
        p->col = c;
        p->v = v;
        p->right = NULL;
        p->down = NULL;

        // 插入到行链表中
        if (M->rhead[r] == NULL) {
            M->rhead[r] = p;
        } else {
            for (q = M->rhead[r]; q->right && q->right->col < c; q = q->right);
            p->right = q->right;
            q->right = p;
        }

        // 插入到列链表中
        if (M->chead[c] == NULL) {
            M->chead[c] = p;
        } else {
            for (q = M->chead[c]; q->down && q->down->row < r; q = q->down);
            p->down = q->down;
            q->down = p;
        }
    }
}

// 显示稀疏矩阵
void ShowMatrix(CrossList *M) {
    int i, j;
    OLNode *p;
    for (i = 1; i <= M->m; i++) {
        p = M->rhead[i];
        if (M->rhead[i]) {
            for (j = 1; j <= M->n; j++) {
                if (p && p->col == j) {
                    printf("%d   ", p->v);
                    p = p->right;
                } else {
                    printf("0   ");
                }
            }
        } else {
            for (j = 1; j <= M->n; j++) {
                printf("0   ");
            }
        }
        printf("\n");
    }
}

// 查找列链表中指定位置的前驱节点
OLNode *colpred(int r, int c, CrossList *M) {
    OLNode *s;
    s = M->chead[c];
    while (s->down != NULL && s->down->row < r)
        s = s->down;
    return s;
}

// 矩阵相加
void AddMatrix(CrossList *M1, CrossList *M2, CrossList *M) {
    OLNode *p, *q, *ra, *rb, *pa, *pb, *qa;
    int i, j;
    if (M1->m != M2->m || M1->n != M2->n) {
        printf("两个矩阵不能相加!\n");
        return;
    }

    // 初始化矩阵M的行和列头指针数组
    for (j = 1; j <= M1->n; j++)
        M->chead[j] = M1->chead[j];
    M->m = M1->m;
    M->n = M1->n;

    for (i = 1; i <= M1->m; i++) {
        ra = M1->rhead[i];
        rb = M2->rhead[i];
        pa = ra;
        pb = rb;
        qa = NULL;

        if (M2->rhead[i] == NULL) {
            M->rhead[i] = M1->rhead[i];
        } else {
            while (pb) {
                if (pa && pa->col < pb->col) {
                    if (qa == NULL)
                        M->rhead[i] = pa;
                    qa = pa;
                    pa = pa->right;
                } else if (!pa || pa->col > pb->col) {
                    p = (OLNode *)malloc(sizeof(OLNode));
                    *p = *pb;
                    if (qa == NULL)
                        M->rhead[i] = p;
                    else
                        qa->right = p;
                    p->right = pa;
                    qa = p;

                    if (M->chead[pb->col] == NULL) {
                        M->chead[pb->col] = p;
                        p->down = NULL;
                    } else {
                        q = colpred(p->row, p->col, M);
                        p->down = q->down;
                        q->down = p;
                    }
                    pb = pb->right;
                } else {
                    pa->v += pb->v;
                    if (pa->v == 0) {
                        if (qa == NULL)
                            M->rhead[i] = pa->right;
                        else
                            qa->right = pa->right;
                        if (M->chead[pb->col] == pa)
                            M->chead[pb->col] = pa->down;
                        else {
                            q = colpred(pa->row, pa->col, M);
                            q->down = pa->down;
                        }
                        free(pa);
                    } else {
                        if (qa == NULL)
                            M->rhead[i] = pa;
                        qa = pa;
                    }
                    pa = pa->right;
                    pb = pb->right;
                }
            }
        }
    }
}

int main() {
    CrossList A, B, C;
    CreateCrossList(&A); // 创建第一个稀疏矩阵
    printf("第一个矩阵为:\n");
    ShowMatrix(&A); // 显示第一个稀疏矩阵

    CreateCrossList(&B); // 创建第二个稀疏矩阵
    printf("第二个矩阵为:\n");
    ShowMatrix(&B); // 显示第二个稀疏矩阵

    AddMatrix(&A, &B, &C); // 矩阵相加
    printf("相加的结果为:\n");
    ShowMatrix(&C); // 显示相加后的矩阵

    return 0;
}

在数据结构中,稀疏矩阵是一种特殊的矩阵,其中大部分元素为零。对于稀疏矩阵,传统的二维数组存储方式会浪费大量空间。因此,我们通常使用十字链表来存储稀疏矩阵,从而节省存储空间。本文将介绍如何使用C语言实现稀疏矩阵的十字链表表示、显示和矩阵相加操作。

一、稀疏矩阵的十字链表表示

十字链表是一种高效的数据结构,用于存储稀疏矩阵。每个非零元素用一个节点表示,节点包含行号、列号、值,以及指向右边和下边节点的指针。我们定义两个结构体:OLNode 用于存储单个非零元素的信息,CrossList 用于存储整个稀疏矩阵。          

typedef struct OLNode {
    int row, col; // 行号和列号
    int v; // 值
    struct OLNode *right, *down; // 指向右边和下边的节点指针
} OLNode, *OLink;

typedef struct {
    OLNode *rhead[MAX], *chead[MAX]; // 行和列的头指针数组
    int m, n, t; // 矩阵的行数、列数和非零元素个数
} CrossList;

二、稀疏矩阵的初始化

我们通过用户输入来初始化稀疏矩阵。用户依次输入矩阵的行数、列数和非零元素个数,然后输入每个非零元素的行号、列号和值。程序会将这些非零元素插入到十字链表中。

void CreateCrossList(CrossList *M) {
    int m, n, t, i, r, c, v;
    OLNode *p, *q;
    printf("输入矩阵行数、列数和非零元素个数:");
    scanf("%d%d%d", &m, &n, &t);
    M->m = m;
    M->n = n;
    M->t = t;

    // 初始化行和列的头指针数组
    for (i = 1; i <= m; i++)
        M->rhead[i] = NULL;
    for (i = 1; i <= n; i++)
        M->chead[i] = NULL;

    // 输入非零元素并插入到十字链表中
    for (i = 1; i <= t; i++) {
        printf("输入第%d个非零元素的行号、列号和值:", i);
        scanf("%d%d%d", &r, &c, &v);
        p = (OLNode *)malloc(sizeof(OLNode));
        p->row = r;
        p->col = c;
        p->v = v;
        p->right = NULL;
        p->down = NULL;

        // 插入到行链表中
        if (M->rhead[r] == NULL) {
            M->rhead[r] = p;
        } else {
            for (q = M->rhead[r]; q->right && q->right->col < c; q = q->right);
            p->right = q->right;
            q->right = p;
        }

        // 插入到列链表中
        if (M->chead[c] == NULL) {
            M->chead[c] = p;
        } else {
            for (q = M->chead[c]; q->down && q->down->row < r; q = q->down);
            p->down = q->down;
            q->down = p;
        }
    }
}

示例输入

假设用户输入以下非零元素:

输入矩阵行数、列数和非零元素个数:3 3 4
输入第1个非零元素的行号、列号和值:1 2 5
输入第2个非零元素的行号、列号和值:2 1 3
输入第3个非零元素的行号、列号和值:3 3 7
输入第4个非零元素的行号、列号和值:2 3 2

三、稀疏矩阵的显示

为了方便查看稀疏矩阵的内容,我们编写了一个函数来显示矩阵。该函数按照行列顺序遍历矩阵,输出非零元素及其位置,其余位置输出0。

void ShowMatrix(CrossList *M) {
    int i, j;
    OLNode *p;
    for (i = 1; i <= M->m; i++) {
        p = M->rhead[i];
        if (M->rhead[i]) {
            for (j = 1; j <= M->n; j++) {
                if (p && p->col == j) {
                    printf("%d   ", p->v);
                    p = p->right;
                } else {
                    printf("0   ");
                }
            }
        } else {
            for (j = 1; j <= M->n; j++) {
                printf("0   ");
            }
        }
        printf("\n");
    }
}

示例输出

第一个矩阵:

0   5   0
3   0   2
0   0   7

四、稀疏矩阵的相加

稀疏矩阵的相加操作是将两个矩阵的对应元素相加。我们通过遍历两个矩阵的非零元素,进行相加操作,并将结果存储到一个新的十字链表中。                                            

void AddMatrix(CrossList *M1, CrossList *M2, CrossList *M) {
    OLNode *p, *q, *ra, *rb, *pa, *pb, *qa;
    int i, j;
    if (M1->m != M2->m || M1->n != M2->n) {
        printf("两个矩阵不能相加!\n");
        return;
    }

    // 初始化结果矩阵的行和列头指针数组
    for (j = 1; j <= M1->n; j++)
        M->chead[j] = M1->chead[j];
    M->m = M1->m;
    M->n = M1->n;

    for (i = 1; i <= M1->m; i++) {
        ra = M1->rhead[i];
        rb = M2->rhead[i];
        pa = ra;
        pb = rb;
        qa = NULL;

        if (M2->rhead[i] == NULL) {
            M->rhead[i] = M1->rhead[i];
        } else {
            while (pb) {
                if (pa && pa->col < pb->col) {
                    if (qa == NULL)
                        M->rhead[i] = pa;
                    qa = pa;
                    pa = pa->right;
                } else if (!pa || pa->col > pb->col) {
                    p = (OLNode *)malloc(sizeof(OLNode));
                    *p = *pb;
                    if (qa == NULL)
                        M->rhead[i] = p;
                    else
                        qa->right = p;
                    p->right = pa;
                    qa = p;

                    if (M->chead[pb->col] == NULL) {
                        M->chead[pb->col] = p;
                        p->down = NULL;
                    } else {
                        q = colpred(p->row, p->col, M);
                        p->down = q->down;
                        q->down = p;
                    }
                    pb = pb->right;
                } else {
                    pa->v += pb->v;
                    if (pa->v == 0) {
                        if (qa == NULL)
                            M->rhead[i] = pa->right;
                        else
                            qa->right = pa->right;
                        if (M->chead[pb->col] == pa)
                            M->chead[pb->col] = pa->down;
                        else {
                            q = colpred(pa->row, pa->col, M);
                            q->down = pa->down;
                        }
                        free(pa);
                    } else {
                        if (qa == NULL)
                            M->rhead[i] = pa;
                        qa = pa;
                    }
                    pa = pa->right;
                    pb = pb->right;
                }
            }
        }
    }
}

第二个矩阵:

复制

0   0   0
0   0   2
0   0   7

相加的结果:

复制

0   5   0
3   0   4
0   0   14

关键点解释

  1. 相加逻辑

    • 如果两个矩阵的行和列不匹配,则无法进行相加操作。

    • 对于每个非零元素,根据其位置(行号和列号)进行相加。

    • 如果某一行或列中只有一个矩阵有非零元素,则直接将该非零元素复制到结果矩阵中。

    • 如果两个矩阵在相同位置都有非零元素,则将它们的值相加,并将结果存储到结果矩阵中。

  2. 十字链表的插入操作

    • 在行链表和列链表中插入节点时,需要维护链表的有序性。

    • 使用前驱节点(qa)来帮助插入操作,确保插入的节点能够正确连接到链表中。

  3. 内存管理

    • 在节点值相加为零时,需要释放该节点的内存,避免内存泄漏。

五、完整实现步骤

1. 创建稀疏矩阵

用户输入矩阵的行数、列数和非零元素个数,然后依次输入每个非零元素的行号、列号和值。程序将这些非零元素插入到十字链表中。

2. 显示稀疏矩阵

按照行列顺序遍历矩阵,输出非零元素及其位置,其余位置输出0。

3. 矩阵相加

  • 检查两个矩阵的行数和列数是否相同。

  • 遍历两个矩阵的非零元素,进行相加操作。

  • 将结果存储到一个新的十字链表中。

六、运行示例

输入

复制

输入矩阵行数、列数和非零元素个数:3 3 4
输入第1个非零元素的行号、列号和值:1 2 5
输入第2个非零元素的行号、列号和值:2 1 3
输入第3个非零元素的行号、列号和值:3 3 7
输入第4个非零元素的行号、列号和值:2 3 2
输入矩阵行数、列数和非零元素个数:3 3 2
输入第1个非零元素的行号、列号和值:2 3 2
输入第2个非零元素的行号、列号和值:3 3 7

输出

第一个矩阵:

复制

0   5   0
3   0   2
0   0   7

第二个矩阵:

复制

0   0   0
0   0   2
0   0   7

相加的结果:

0   5   0
3   0   4
0   0   14

七、总结

通过本文,我们学习了如何使用C语言实现稀疏矩阵的十字链表表示、显示和矩阵相加操作。十字链表是一种高效的稀疏矩阵存储方式,能够节省大量空间。矩阵相加操作通过遍历两个矩阵的非零元素实现,逻辑清晰且高效。希望本文对大家有所帮助!

你可能感兴趣的:(数据结构,c语言,开发语言)