数据结构----矩阵的压缩存储

本文介绍矩阵压缩存储的概念及其应用,包括对称矩阵、三角矩阵、对角矩阵和稀疏矩阵等不同类型矩阵的压缩存储方法。重点讲解了稀疏矩阵的三元组表存储方式和十字链表存储方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

矩阵的压缩存储


矩阵压缩:

指为多值相同的元素只分配一个存储空间

对零元素不分配空间

特殊矩阵的压缩存储
  • 对称矩阵

    • 定义:A[i][j] = A[j][i]

      开辟空间大小:n * n 个元素压缩至n*(n-1)/2个元素

    • 使用一维数组s[k]以行来存储对称矩阵A(i,j)的下三角元素,则

      在这里插入图片描述

    • 存储与实现:

      #include <stdio.h>
      #include <stdlib.h>
      #include <malloc.h>
      #include <string.h>
      /**对称矩阵压缩存储**/
      void PrintMatrix(double *s,int n);
      /**存储对称矩阵**/
      void storeMatrix(int n){
      	double *s = new double[n];
      	int len = n*(n+1)/2;
      	printf("Input the UP_Matrix's element by the order of row\n");
      	for(int i = 0;i<len;i++){
      		scanf("%d",&s[i]);
      	}
      	PrintMatrix(s,n);
      }
      /**打印矩阵**/
      void PrintMatrix(double *s,int n){
      	int k = 0;
      	for(int i =0;i<n;i++){
      		for(int j = 0;j<n;j++){
      			//以行为主存储下三角矩阵
      			if(i>=j)	k = i*(i+1)/2+j;
      			else	k = j*(j+1)/2+i;
      			printf("%d\t",s[k]);
      		}
      		printf("\n");
      	}
      	
      }
      void main(){
      	int n = 0 ;
      	printf("Input the order of the matrix:");
      	cin >> n;
      	storeMatrix(n);
      	system("pause");
      }
      
  • 三角矩阵

    • 分为上三角矩阵,下三角矩阵
    • 与对称矩阵差别不大,仅在上三角矩阵中 矩阵的存储相当于对称矩阵的以列为主的压缩存储k = i*(2n-i+1)/2 + i
  • 对角矩阵

    • 所有非零元素集中在主对角线两侧的带状区域内。m对角矩阵是指非零元素在每行中有m个。

在这里插入图片描述

  • 特点:

    • 第一行和最后一行有两个非零元素,第2~n-1行有3个非零元素
    • 使用一维数组存储,需要存储3*n-2个元素
    • 一维数组s[k]A[i][j]的对应关系为:k = 2*i+j
稀疏矩阵的压缩存储

稀疏矩阵:非零元较零元少,且分布没有一定规律的矩阵

压缩存储原则:只存矩阵行列维数,和非零元的行列下标及值

  • 三元组表

    • 即用顺序存储结构表示三元组表,记录非零元素的行和列号

    • 类型定义【行优先存储】:

      • #define MAXSIZE 100
        Typedef struct{
            int i,j;
            ElemType e;
        }Tripe;
        
        Typedef struct{
            Tripe data[MAXSIZE+1];
            int mu,nu,tu;
        }TSMatrix;
        
    • 转置的实现:

      • 方法一、常规思路

        • 将矩阵的行、列维数互换
        • 将每个三元组中的i 和 j 相互调换
        • 重排三元组次序
        bool TransposeSMatrix(TSMatrix M, TSMatrix T)
        {
            T.mu = M.nu;T.nu = M.mu; T.tu = M.tu;
            if(M.tu)
            {
                q = 1;
                for(int col = 1; col <= M.mu; col++)
                {
                    for(int p = 1; p <= M.tu; p++)
                    {
                        if(M.data[p].j == col)
                        {
                            T.data[q].i = M.data[p].j;
                            T.data[q].j = M.data[p].i;
                            T.data[q].e = M.data[p].e;
                            q++;
                        }
                    }
                }
             }else 
                return false;
            return true;
        }
        

        其实,效率并不高,基本操作是将M.data中的元素转到T.data中,利用两个循环完成,多次遍历M.data。

        所以,我们就想,能不能在只扫描一次的基础上,完成M到T的转置?

      • 方法二、快速转置算法

        • M中非零元以行为主序存储,故各列的分零元的存储位置不是“连续”,但是,存储顺序却是一致的。

        • 思路:

          • M中各列非零元个数num[ ];
          • M中各列第一个非零元在T.data中的下标cpos[ ];
          • M.data进行一次扫描, 遇到col列的第一个非零元三元组时,按cpos[col ]的位置,将其放至T.data中,当再次遇到col列的非零元三元组时,只须顺序放到col列元素的后面
        • bool FastTransposeSMatrix(TSMatrix M, TSMatrix T)
          {
              T.mu=M.nu; T.nu=M.mu; T.tu=M.tu;
          	if(M.tu)
              {
                 for (col=1; col<=M.nu; ++col)   			        	
                     	num[col]=0;
                 for (t=1; t<=M.tu; ++t) 
                      ++num[M.data[t].j];      //求M中每一列非零元个数
              //求第 col列中第一个非零元在T.data中的序号
              cpot[1]=1;
              for(col=2; col<=M.tu;  ++col) 
                  cpot[col]=cpot[col-1]+num[col-1];
              for(p=1; p<M.tu;  ++p) 
              {  
                   col=M.data[p].j;   
                   q=cpot[col]; 
                   T.data[q].i=M.data[p].j;  
                   T.data[q].j=M.data[p].i; 
                   T.data[q].e=M.data[p].e;   
                   ++cpot[col];
              }
              }
          }//只有在tu非常小的时候才有意义
          
  • 十字链表

    • 当矩阵的非零元个数和位置在操作过程中变化较大时,采用链式存储的方式,设行指针数组和列指针数组,分别指向行、列第一个非零元,构成十字链表。

    • 在这里插入图片描述

    • 类型定义:

      typedef struct OLNode{
          int i,j;
          ElemType e;
          struct OLNode *right,*down;
      }OLNode,*OList;
      
      typedef struct{
          OLink *Rhead,*Chead;
          int mu,nu,tu;
      }CrossList;
      
    • 建立稀疏矩阵并用十字链表存储

      bool CreateSMatrix(CrossList M)
      {
          OLNode *p,*q;
          cin >> m >> n >> t;//读入行、列及非零元个数
          M->mu = m;M->nu = n;M->tu = t;
          //声明指针数组,并初始化为NULL
          M->Rhead = (OLink*)malloc(m*sizeof(OLink));
          M->Chead = (OLink*)malloc(n*sizeof(OLink));
          for(int k = 0; k < m; k++)
              M->Rhead[k] = NULL;
          for(int k = 0; k < m; k++)
              M->Chead[k] = NULL;
          /*下面进行结点的输入
          操作为 拿到一个结点,分别插入 行链数组相应的行链表,及列链数组相应的列链表,插入两种情况【以行链表为例】,一种为行链表为空,或是要插入结点列大小小于链表已有结点的列大小,插在前面;另一种为 列数较大,需插在后面,一个循环去搜应该插到哪里
          */
          for(int index = 0; index < t; index++)
          {
              cin >> i >> j >> e;
              p = (OLink)malloc(sizeof(OLNode));
              p->i = i;
              p->j = j;
              p->e = e;
              if(M->Rhead[i] == NULL || M->Rhead[i]->j > j)
              {
                  p->right = M->Rhead[i];
              	M->Rhead[i] = p;
              }else
              {
                  for(q = M->Rhead[i];q->right && q->right->j < j;q = q->right)
                  ;
              p->right = q->right;
              q->right = p;
              }
              if(M->Chead[j] == NULL || M->Chead[j]->i > i)
              {
                  p->down = M->Chead[j];
                  M->Chead[j] = p;
              }else 
              {
                  for(q = M->Chead[j]; q->down && q->down->i < i; q = q->down)
                      ;
                  p->down = q->down;
                  q->down = p;
              }
          }
          return true;
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值