数据结构的学习_4.2 矩阵的压缩存储(对称矩阵)

4.2 矩阵的压缩存储(一)

在有些情况下,矩阵中含有许多值相同或者值为零的元素,如果还按前面的方法来存储这种矩阵,就会产生大量的空间浪费。为了节省存储空间,可以对这类矩阵采用压缩存储。

4.2.1 特殊矩阵

所谓特殊矩阵,指的是相同值的元素或者零元素在矩阵中的分布有一定规律的矩阵。

1.对称矩阵

若n阶方阵A中的元素满足下述性质:
a i j = a j i ( 0 ≤ i , j ≤ n − 1 ) a_{ij}=a_{ji} (0≤i,j≤n-1) aij=aji(0ijn1)
则称A为n阶的对称矩阵。对称矩阵中的元素是关于主对角线对称的,所以只需要存储矩阵上三角或者下三角的元素即可。让两个对称的元素共享一个存储空间。这样,就能够节省近一半的存储空间。

原来数据结构这么厉害,怪不得必须得学。。。

[ a 00 a 10 a 11 a 20 a 21 a 22 . . . . . . . . . a n − 10 a n − 11 . . . a n − 1 n − 1 ] \begin{bmatrix} a_{00}&&&&\\ a_{10}&a_{11}\\ a_{20}&a_{21}&a_{22}\\ ...&...&...&\\ a_{n-10}&a_{n-11}&...&a_{n-1n-1}\\ \end{bmatrix} a00a10a20...an10a11a21...an11a22......an1n1

​ 对称矩阵下三角元素示意图

在以上的下三角矩阵中,第i行(0≤i≤m-1)恰好有i+1个元素,所以元素总数为
∑ i = 0 n − 1 ( i + 1 ) = n ( n + 1 ) / 2 \sum_{i=0}^{n-1}(i+1)=n(n+1)/2 i=0n1(i+1)=n(n+1)/2

刚看到n(n+1)/2这个公式的时候有点熟,好像在哪里见过。百度了下,就是当年1+2+3+…+100=?这个题所使用的公式吗!!100个100+1然后再除以2。。。

这样看就明白了,原谅我还是没把这思维带入到里面。。。

假设以一维数组sa[n(n+1)/2]作为n阶对称矩阵A的存储结构,那么,矩阵中元素
a i j a_{ij} aij
和数组元素sa[k]之间存在着一 一对应关系。

这个下标ij不好表示到一行中,如果有比较好的工具麻烦跟我说下。

这种对应关系分析如下:

  1. 若i≥j时,则a _ {ij}在下三角矩阵中。

    a _{ij}之前i行(从0行到第i-1行)一共有1+2+3+...+i=i * (i+1) /2个元素,在第i行上,a _{ij}之前恰好有j个元素。

k = i ∗ ( i + 1 ) / 2 + j                  ( 0 ≤ k < n ( n + 1 ) / 2 ) k=i * (i + 1) / 2 + j \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ( 0≤ k <n (n+1) / 2 ) k=i(i+1)/2+j                (0k<n(n+1)/2)

  1. 若i<j时,则a_{ij}在上三角矩阵中。

    因为有
    a i j = a j i a_{ij}=a_{ji} aij=aji
    所以只要交换i和j即可得到:
    k = j ∗ ( j + 1 ) / 2 + i                  ( 0 ≤ k < n ( n + 1 ) / 2 ) k=j * (j + 1) / 2 + i \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ( 0≤ k <n (n+1) / 2 ) k=j(j+1)/2+i                (0k<n(n+1)/2)

因此,有:
k = { j ∗ ( j + 1 ) 2 + i        i < j , i ∗ ( i + 1 ) 2 + j        i ≥ j , 0 ≤ k < n ( n + 1 ) / 2 k=\begin{cases} \end{cases}^{i*(i+1)2+j\ \ \ \ \ \ i≥j,}_{j*(j+1)2+i\ \ \ \ \ \ i<j,}0≤k<n (n+1) / 2 k={j(j+1)2+i      i<ji(i+1)2+j      ij0k<n(n+1)/2

由此,a_{ij}的存储地址可用下面的公式计算:

L O C ( a i j ) = L O C ( s a [ k ] ) = L O C ( s a [ 0 ] ) + k ∗ d LOC(a_{ij})=LOC(sa[k])=LOC(sa[0])+k*d LOC(aij)=LOC(sa[k])=LOC(sa[0])+kd

LOC代表的含义是存储地址,sa[0]是第一个元素,k代表元素个数,d代表一个元素需要d个存储单元

有了上述的计算公式,就能够立即找到矩阵元素a_{ij}在其压缩存储表示sa中的对应位置k。例如,
a 32 和 a 23 a_{32}和a_{23} a32a23
都存储在sa[8]中,这是因为
k = i ∗ ( i + 1 ) / 2 + j = 3 ∗ ( 3 + 1 ) / 2 + 2 = 8 k=i*(i+1)/2+j=3*(3+1)/2+2=8 k=i(i+1)/2+j=3(3+1)/2+2=8

例4.3

已知A和B是两个n * n阶的对称矩阵,因为是对称矩阵,所以仅需要输入下三角元素值存入一维数组。试写一算法,求对称矩阵A和B的乘积。

  • 分析:

    如果是两个完整的矩阵相乘,其算法是比较简单的,但由于是对称矩阵,所以要清楚对称矩阵的第i行和第j列的元素数据在一维数组中的位置,其位置计算公式为:
    l = i ∗ ( i + 1 ) / 2 + j        当 i ≥ j 时 ( A i j , B i j 处 于 下 三 角 中 ) ; l=i*(i+1)/2 +j \ \ \ \ \ \ 当i≥j时(A_{ij},B_{ij}处于下三角中); l=i(i+1)/2+j      ijAij,Bij;

    l = j ∗ ( j + 1 ) / 2 + i        当 i < j 时 ( A i j , B i j 处 于 上 三 角 中 ) ; l=j*(j+1)/2 +i \ \ \ \ \ \ 当i<j时(A_{ij},B_{ij}处于上三角中); l=j(j+1)/2+i      i<jAij,Bij;

    期中,l代表A_{ij}B_{ij}在其对称矩阵中的位置,而且0≤l<n(n+1)/2。因此,实现本题功能的算法如下:

    void matrixmult(int a[] ,int b[] ,int c[][20],int n){
        //n为A、B矩阵下三角元素个数,a,b分别为一维数组,存放矩阵A和B的下三角元素值
        //c存放A和B的乘积
        for(i=0;i<20;i++){
            for(j=0;j<20;j++){
                s=0;
                for(k=0;k<n;k++){
                    if(i>=k){		//表示元素为下三角的元素,计算在a数组中的下标
                        l1=i*(i+1)/2+k;
                    }else {			//表示元素为上三角的元素,计算下标
                        l1=k*(k+1)/2+i;
                    }
                    if(k>=j){		//表示元素为下三角的元素,计算在b数组中的下标
                        l2=k*(k+1)/2+j;
                    }else {
                        l2=j*(j+1)/2+k;
                    }
                    s=s+a[l1]*b[l2];
                }
                c[i,j]=s;
            }
        }
    }
    

这例子是啥呀,看都看不懂。心态炸裂。无能狂怒。n代表一个矩阵下三角元素个数?假设20的对阶,个数是20 * (20 +1)/2=210 一半的话也有100多,这循环?????

不敢否认这书的正确性,毕竟是自考指定教材。敢不敢再写详细点。。。。。

算了,略过这个。考试要是考了当我送给你的。

好气啊 。 。。 。。。。。 。。。。。。。。 。。。。。。。。。。。。 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

  • 14
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
稀疏矩阵是指矩阵中大部分元素为0的矩阵,而非0元素的数量相对较少。由于这种矩阵的特殊性质,我们可以采用压缩存储的方式来节省存储空间。常用的压缩存储方式有三种:行逐行压缩、列逐列压缩和十字链表压缩。下面以行逐行压缩为例,介绍C语言中稀疏矩阵压缩存储及其应用。 行逐行压缩是指将稀疏矩阵的每一行转化为一个三元组(i, j, A[i][j]),其中i和j分别表示非零元素的行列下标,A[i][j]表示该元素的值。这样,我们就可以用一个一维数组来存储整个稀疏矩阵。具体的实现代码如下: ``` #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct { int row; int col; int val; } Triple; void create_sparse_matrix(int rows, int cols, int *matrix, int size, Triple *sparse_matrix) { int i, j, k = 0; for (i = 0; i < rows; ++i) { for (j = 0; j < cols; ++j) { if (matrix[i * cols + j] != 0) { sparse_matrix[k].row = i; sparse_matrix[k].col = j; sparse_matrix[k].val = matrix[i * cols + j]; ++k; } } } sparse_matrix[size].row = rows; sparse_matrix[size].col = cols; sparse_matrix[size].val = k; } void print_sparse_matrix(Triple *sparse_matrix, int size) { int i; printf("行\t列\t值\n"); for (i = 0; i <= size; ++i) { printf("%d\t%d\t%d\n", sparse_matrix[i].row, sparse_matrix[i].col, sparse_matrix[i].val); } } int *sparse_matrix_multiplication(Triple *a, int a_size, Triple *b, int b_size) { if (a[0].col != b[0].row) { return NULL; } int i, j, k; int *c = (int*)malloc(a[0].row * b[0].col * sizeof(int)); for (i = 0; i < a[0].row; ++i) { for (j = 0; j < b[0].col; ++j) { c[i * b[0].col + j] = 0; for (k = 0; k < a_size; ++k) { if (a[k].row == i && b[k].col == j) { c[i * b[0].col + j] += a[k].val * b[k].val; } } } } return c; } int main() { int rows, cols, i, j; int matrix[MAX_SIZE][MAX_SIZE], size; Triple *sparse_matrix; printf("请输入矩阵的行数和列数:"); scanf("%d%d", &rows, &cols); printf("请输入矩阵的所有元素:\n"); for (i = 0; i < rows; ++i) { for (j = 0; j < cols; ++j) { scanf("%d", &matrix[i][j]); } } size = 0; for (i = 0; i < rows; ++i) { for (j = 0; j < cols; ++j) { if (matrix[i][j] != 0) { ++size; } } } sparse_matrix = (Triple*)malloc((size + 1) * sizeof(Triple)); create_sparse_matrix(rows, cols, (int*)matrix, size, sparse_matrix); print_sparse_matrix(sparse_matrix, size); free(sparse_matrix); return 0; } ``` 在这个代码中,我们首先定义了一个三元组`Triple`来表示稀疏矩阵的一个非零元素,其中row和col分别表示行列下标,val表示元素值。然后定义了三个函数,`create_sparse_matrix`用于将原始矩阵转化为稀疏矩阵,`print_sparse_matrix`用于打印稀疏矩阵,`sparse_matrix_multiplication`用于计算两个稀疏矩阵的乘积。 在`create_sparse_matrix`函数中,我们首先遍历整个原始矩阵,找到所有非零元素,并将其转化为一个三元组,存储在稀疏矩阵中。最后,我们在稀疏矩阵的最后一行,存储原始矩阵的行列数和稀疏矩阵中非零元素的个数。在`print_sparse_matrix`函数中,我们直接遍历稀疏矩阵,打印每个三元组的行列下标和元素值。在`sparse_matrix_multiplication`函数中,我们首先判断两个矩阵是否可以相乘,然后遍历第一个矩阵的所有行和第二个矩阵的所有列,对于每个元素,找到它们在两个稀疏矩阵中的对应位置,并计算它们的乘积,最后存储在结果矩阵中。 稀疏矩阵压缩存储可以大大节省存储空间,特别是当矩阵中非零元素的数量很少时,它的优势更加明显。稀疏矩阵还可以应用于很多实际场景,比如图像处理中的图像压缩、网络流量分析中的路由优化等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值