首页 > 其他分享 >[数据结构] 稀疏矩阵的转置与快速转置

[数据结构] 稀疏矩阵的转置与快速转置

时间:2023-02-19 16:13:59浏览次数:32  
标签:零元 转置 矩阵 稀疏 三元组 一列 数据结构

稀疏矩阵

稀疏矩阵的定义

在矩阵中,若数值为 0 的元素数目远远多于非 0 元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵。

假设在 m * n 的矩阵中,有 t 个非 0 元素,令 δ = t / (m * n) ,则 δ 为矩阵的稀疏因子

稀疏矩阵的压缩存储

三元组表示法

稀疏矩阵由于非 0 元素很少,所以我们需要压缩存储矩阵。

我们只存储矩阵中的非零元。用 (i, j, e) 来表示一个非零元,其中 ij 表示非零元所在行和列,e 表示非零元的值。稀疏矩阵可以由表示非零元的三元组及其行列数唯一确定。

三元组表示示例

三元组表在存储非零元的时候,首先要按照行序顺序排列,如果行相同,那么需要按照列序顺序排列。

三元组表的基本结构

typedef int ElemType;
typedef struct{

    int i, j;
    ElemType e;

}Triple;

typedef struct{

    Triple data[MAXSIZE];
    int mu, nu, tu;          //矩阵行数,列数和非0元个数

}TSMatrix;


稀疏矩阵的转置

稀疏矩阵转置的基本概念

转置运算:一个 m * n 的矩阵 M ,其对应的转置矩阵是一个 n * m 的矩阵 T ,并且 T 中的元素 T(i, j)B 中的元素 M(j, i) 对应相等。

我们需要将三元组的行列互换,要构造一个转置矩阵 T 的三元组表,并且这个三元组表中的次序也要满足按照行为主序排列,按照列为次序排列。

由于 T 中的行对应的是 M 中的列,所以在转置过程中,我们需要顺序枚举每一列。

所以朴素的稀疏矩阵转置方法为:
(1)顺序枚举每一列,在矩阵 M 中查找处于该列的非零元三元组;
(2)将该三元组行列互换放入矩阵 T 的三元组表中。

稀疏矩阵转置的结果

稀疏矩阵转置代码

//稀疏矩阵转置   (适用于 tu << mu × nu 的情况)
void TransposeSMatrix(TSMatrix M,TSMatrix &T){
    T.mu = M.nu;                           //T行数等于原矩阵列数
    T.nu = M.mu;                           //T列数等于原矩阵行数
    T.tu = M.tu;
    if(!T.tu)  return; 
    
    int q = 1;                 //转置矩阵中三元组下标
    for(int col = 1; col <= M.nu; ++col){  //枚举每一列
        for(int p = 1; p <= M.tu; ++p){    //查找M中处于该列的非零元
            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++;
            }
        }
    }
}


稀疏矩阵的快速转置

稀疏矩阵快速转置的基本思路

朴素的稀疏矩阵转置方法是比较低效的,枚举每一列时都要在原矩阵中的三元组表中扫描一遍,如果可以事先直到每一列的非零元在转置矩阵 T 的三元组表中的对应序号,可以大大减小转置的时间复杂度。

我们可以定义一个数组 num[] 来记录原矩阵 M 中每一列非零元的个数, 同时再定义一个数组 cpot[] 用来记录 M 中每一列第一个非零元在 T 中对应的位置。

T 中我们将行列进行了互换,所以在 T 中每一行的第一个非零元对应的是 M 中每一列的第一个非零元。在三元组表中,我们需要按照行主序排列,所以我们要记录 M 中每一列的非零元个数,来知道转置矩阵 T 中每一行的非零元个数,将 M 中同一列的非零元依次排在 T 的三元组表的前面。

为了得到 M 中每一列的第一个非零元在 T 中对应的位置,我们可以得到如下的递推式:

从第二列开始每一列的第一个非零元位置等于前一列的第一个非零元位置加上前一列非零元的个数。
当然第一列可能无非零元,所以 T 中对应位置1有可能延续到后面的某一列中。

稀疏矩阵快速转置代码

//稀疏矩阵的快速转置算法
int cpot[MAXSIZE + 1], num[MAXSIZE + 1];   //辅助数组  
//cpot[col] 表示M中第col列第一个非0元在T.data中的位置
//num[col]  表示M中第col列中非0元的个数
void FastTransposeSMatrix(TSMatrix M, TSMatrix &T){
    T.mu = M.nu;
    T.nu = M.mu;
    T.tu = M.tu;
    if(!T.tu) return;

    for(int col = 1; col <= M.mu; col++)
        num[col] = 0;                      //初始化为0
 
    for(int k = 1; k <= M.tu; k++) 
        num[M.data[k].j]++;                //记录M三元组表每一列列中非0元个数

    cpot[1] = 1;                           //初始化第一个非0元的序号
    for(int col = 2; col <= M.mu; col++)   //求第col列中第一个非零元在T三元组表中的序号   
        cpot[col] = cpot[col - 1] + num[col - 1]; 

    for(int p = 1; p <= M.tu; p++){
        int col = M.data[p].j;             //此时M对应三元组中的非0元的所在列
        int q = cpot[col];                 //q为当前非0元的应当放置的序号位置
        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]++;                       //cpot[col]++,对应下一个此列中非0元的序号
        //cpot[col]最后一直加到等于cpot[col + 1],第col列也就不会有更多的非0元了
    }
}


程序测试

完整程序

点击查看代码
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10000

typedef int ElemType;
typedef struct{

    int i, j;
    ElemType e;

}Triple;

typedef struct{

    Triple data[MAXSIZE];
    int mu, nu, tu;          //矩阵行数,列数和非0元个数

}TSMatrix;


//输入稀疏矩阵数据
void InPutM(TSMatrix &M){
    printf("输入稀疏矩阵的 行数, 列数, 非0元个数 :\n");
    scanf("%d %d %d", &M.mu, &M.nu, &M.tu);
    printf("输入矩阵非0元素的 所在行i, 所在列j, 值e:\n");
    for(int k = 1; k <= M.tu; k++){
        scanf("%d %d %d", &M.data[k].i, &M.data[k].j, &M.data[k].e);
    }
}


//打印稀疏矩阵三元组数据
void PrintM(TSMatrix T){
    printf("  %d    %d    %d\n", T.mu, T.nu, T.tu);
    printf("  ------------\n");
    for(int k = 1; k <= T.tu; k++){
        printf("  %d    %d    %d\n",T.data[k].i, T.data[k].j, T.data[k].e);
    }
}


//稀疏矩阵转置   (适用于 tu << mu × nu 的情况)
void TransposeSMatrix(TSMatrix M,TSMatrix &T){
    T.mu = M.nu;                           //T行数等于原矩阵列数
    T.nu = M.mu;                           //T列数等于原矩阵行数
    T.tu = M.tu;
    if(!T.tu)  return; 
    
    int q = 1;                             //从列数小的开始,一一对应赋值
    for(int col = 1; col <= M.nu; ++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++;
            }
        }
    }
}


//稀疏矩阵的快速转置算法
int cpot[MAXSIZE + 1], num[MAXSIZE + 1];   //辅助数组  
//cpot[col] 表示M中第col列第一个非0元在T.data中的位置
//num[col]  表示M中第col列中非0元的个数
void FastTransposeSMatrix(TSMatrix M, TSMatrix &T){
    T.mu = M.nu;
    T.nu = M.mu;
    T.tu = M.tu;
    if(!T.tu) return;

    for(int col = 1; col <= M.mu; col++)
        num[col] = 0;                      //初始化为0
 
    for(int k = 1; k <= M.tu; k++) 
        num[M.data[k].j]++;                //记录M三元组表每一列列中非0元个数

    cpot[1] = 1;                           //初始化第一个非0元的序号
    for(int col = 2; col <= M.mu; col++)   //求第col列中第一个非零元在T三元组表中的序号   
        cpot[col] = cpot[col - 1] + num[col - 1]; 

    for(int p = 1; p <= M.tu; p++){
        int col = M.data[p].j;             //此时M对应三元组中的非0元的所在列
        int q = cpot[col];                 //q为当前非0元的应当放置的序号位置
        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]++;                       //cpot[col]++,对应下一个此列中非0元的序号
        //cpot[col]最后一直加到等于cpot[col + 1],第col列也就不会有更多的非0元了
    }
}

int main(){
   TSMatrix M, T, FT;
   printf("————稀疏矩阵转置测试————\n\n");
   InPutM(M);
   printf("\n稀疏矩阵转置前三元组: \n");
   PrintM(M);

   printf("\n稀疏矩阵转置结果: \n");
   TransposeSMatrix(M, T);
   PrintM(T);

   printf("\n稀疏矩阵的快速转置结果: \n");
   FastTransposeSMatrix(M, FT);
   PrintM(FT);
}

测试结果

标签:零元,转置,矩阵,稀疏,三元组,一列,数据结构
From: https://www.cnblogs.com/MAKISE004/p/17111815.html

相关文章

  • 【数据结构】八种常见数据结构介绍
    数据结构是计算机存储、组织数据的方式。一种好的数据结构可以带来更高的运行或者存储效率。数据在内存中是呈线性排列的,但是我们可以使用指针等道具,构造出类似“树形”的......
  • 01 数据结构概念简述
     一、数据结构就是数据的存储方式如何存储、以体现数据之间的逻辑关系,为以后更好的利用数据做准备数据关系一般分为:"一对一"、"一对多"、"多对多""一对一"关系:使......
  • 矩阵旋转
    旋转方法例子对于顺时针旋转90度先上线对称选择,在主对角线交换classSolution{public:voidrotate(vector<vector<int>>&matrix){intn=matrix.si......
  • 从矩阵的谱半径到神经网络梯度消失
    一、矩阵的范数 二、矩阵的谱半径虽然,谱半径小于等于任意矩阵范数。但是,也必存在一个算子范数,小于等于谱半径+一个小的正数 从线性方程组的迭代法的收敛性......
  • Python学习之线性数据结构(二)
    print(end='')end=表示语句结束后加入的东西print(sep='')sep表示间隔符1223这个间隔的空格就是间隔符print(1,2,sep='',end='')#打印数字1和2间隔符为空格......
  • List集合-数据结构
    List集合-数据结构数据结构是计算机存储,组织数据的方式.是指相互之间存在一种或多种特定关系的数据元素的集合.通常情况下,精心选择的数据结构可以带来更高的运行或者......
  • 数据结构
    数组地址的计算1维数组,默认是行优先,也就是先横着放。2位数组行优先,相当于最外围数组横着放,列优先就是最里面的先横着放。稀疏矩阵图(没懂)顺序表和链表队列有......
  • [数据结构] AVL树
    AVL树的基本概念AVL树的定义AVL树得名于它的发明者G.M.Adelson-Velsky和E.M.Landis。AVL树本质上是一颗二叉搜索树,并且本身带有平衡的条件,即每个结点的左右子树的高......
  • 数据结构可视化神器推荐
    旧金山大学做的BPlusTreeVisualization模型​​数据结构可视化​​......
  • 数据结构刷题2023.02.18小记
    连通分量一个无向图的连通分量是其极大的连通子图无向图中任意两个节点之间有连通,则称为连通图。每一个非连通图可分为几个极大连通部分,每一个极大连通子图称为连通分量;......