一 稀疏矩阵的定义
矩阵是如今很多科学与工程计算问题中常用的数学对象,矩阵涉及到的计算通常会出现矩阵的阶数比较高但是非零元素的个数却比较少的情况,因此,我们需要有一种方法来压缩这种比较稀疏的矩阵。
那么,首先第一个问题就是如何定义一个矩阵是否是稀疏的?参考严蔚敏的数据结构教材,第96页给出了稀疏矩阵的定义:假设在m×n的矩阵中,有t个元素不为零,令δ=t/(m+n),称δ为矩阵的稀疏因子。通常认为δ<=0.05时称为稀疏矩阵。
二 矩阵的压缩存储
那么,如何进行稀疏矩阵的压缩存储呢?
按照压缩存储的概念,只要存储矩阵中的非零元素的信息就好了,同时还需要保证根据存储的信息能唯一的确定一个矩阵。因此,采用三元祖(行,列,元素值)再加上矩阵的行、列值就可以解决这个问题。由三元组的不同表示方法可引出稀疏矩阵不同的压缩存储方法,比如说有:三元组顺序表、行逻辑链接的顺序表、十字链表等。本文暂时只讨论三元组顺序表的情况。
三元组顺序表的存储表示如下:
#define MAXSIZE 12500
typedef struct {
int i,j; //元素的行下标和列下标
ElemType e; //元素值
}Triple;
typedef struct{
Triple data[MAXSIZE+1]; //非零元素的三元组
int mu,nu,tu; //矩阵的行数、列数和非零元个数
}TSMatrix;
三 矩阵的转置算法
矩阵的转置运算是一种最简单的矩阵运算,接下来介绍在稀疏矩阵存储为三元组形式下的两种矩阵转置算法。先从第一种简单的转置算法说起,并配上伪代码,文章末尾附上可运行的C++代码,后续会加附Java代码。
要获取一个矩阵的转置矩阵,我们先来分析一下初始矩阵和转置后矩阵的三元组的差异,这里假设矩阵以行序为主序进行存储,如下图所示。
a要变成b只需要经过3步:(1)将矩阵的行列值相互交换;(2)将每个三元组中的i,j相互调换;(3)重组三元组之间的次序。前两条是很容易做到的,关键是如何实现第三条。
(1)这里提出第一种解决办法,即引出了第一个算法:按照b.data中三元组的次序来找a.data中对应的三元组进行转置。也就是按照初始矩阵的列序来进行转置操作,文字描述可能会产生歧义,看下面这张图就能明白了。
伪代码如下:
Status TransposeSMatrix(TSMatrix M, TSMatrix &T){
T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
if(T.tu){
q = 1;
for (col = 1; col <= M.nu; ++col)
{
for (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;
}
}
return Ok;
}
}//TransposeSMatrix
伪代码如下:
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T){
T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
if(T.tu){
for (col = 1; col <= M.nu; ++col) num[col] = 0;
for (t = 1; t <= M.nu; ++t) ++num[M.data[t].j];
cpot[1] = 1;
for(col = 2; col <= M.nu; ++col) cpot[col] = cpot[col-1]+num[col-1];
for (p = 1; p <= M.nu; ++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];
}
}
return Ok;
}//TransposeSMatrix
#include
#include
using namespace std;
#define MAXSIZE 1000
typedef struct{
int row, col; //元素所在行,列
int elem; //元素值
}Triple;
typedef struct {
Triple data[MAXSIZE+1];
int row_num, col_num, elem_num; //矩阵的行数,列数,非零元个数
}TSMatrix;
/************************************************************************/
/* 转置算法1,时间复杂度为O(col_num*elem_num) */
/************************************************************************/
void TransposeMatrix(TSMatrix matrix, TSMatrix &transed_matrix){
transed_matrix.row_num = matrix.col_num;
transed_matrix.col_num = matrix.row_num;
transed_matrix.elem_num = matrix.elem_num;
int q = 0;
//按照matrix中列的顺序来遍历matrix
for (int i = 0; i < matrix.col_num; i++)
{
//找到第i列中所有的非零元素,然后加入到transed_matrix中
for(int j = 0; j < matrix.elem_num; j++){
//这里要减一是因为输入的时候按照下标为1开始,存储的时候是从0开始
if((matrix.data[j].col -1) == i){
transed_matrix.data[q].row = matrix.data[j].col;
transed_matrix.data[q].col = matrix.data[j].row;
transed_matrix.data[q].elem = matrix.data[j].elem;
q++;
}
}
}
}//TransposeMatrix
/************************************************************************/
/* 转置算法2,时间复杂度为O(elem_num) */
/************************************************************************/
void FastTransposeMatrix(TSMatrix matrix, TSMatrix &transed_matrix){
transed_matrix.row_num = matrix.col_num;
transed_matrix.col_num = matrix.row_num;
transed_matrix.elem_num = matrix.elem_num;
//添加num和cpot两个变量;
//num[col] : 表示矩阵中第col列中非零元素的个数
//cpot[col] : 矩阵中第col列的第一个非零元素在transed_matrix.data中的恰当位置
/*--------初始化这两个数组---------------*/
vector num;
num.resize(matrix.col_num, 0);
for (int i = 0; i < matrix.elem_num; i++)
{
num[matrix.data[i].col-1]++;
}
vector cpot;
cpot.resize(matrix.col_num, 0);
cpot[0] = 0;
for (int col = 1; col < matrix.col_num; col++)
{
cpot[col] = cpot[col-1]+num[col-1];
}
/*--------------------------------------*/
for (int p = 0; p < matrix.elem_num; p++)
{
int col = matrix.data[p].col-1;
int q = cpot[col];
transed_matrix.data[q].row = matrix.data[p].col;
transed_matrix.data[q].col = matrix.data[p].row;
transed_matrix.data[q].elem = matrix.data[p].elem;
++cpot[col];
}
}//FastTransposeMatrix
//打印矩阵
void printMatrix(TSMatrix matrix){
int max_row = 0, max_col = 0;
//获取矩阵非零元素的最大行数和最大列数
for (int i = 0; i < matrix.elem_num; i++)
{
if (max_row < matrix.data[i].row)
{
max_row = matrix.data[i].row;
}
if (max_col < matrix.data[i].col)
{
max_col = matrix.data[i].col;
}
}
//构造一个二维数组,存的是二维矩阵的元素,只是为了方便输出
vector> tmp_data;
//先初始化这个二维数组
tmp_data.resize(max_row);
for (int i = 0; i < max_row; i++)
{
tmp_data[i].resize(max_col, 0);
}
//将三元组中的信息添加到二维数组中
for (int i = 0; i < matrix.elem_num; i++)
{
Triple tri;
tri.col = matrix.data[i].col - 1;
tri.row = matrix.data[i].row - 1;
tri.elem = matrix.data[i].elem;
tmp_data[tri.row][tri.col] = tri.elem;
}
for (int i = 0; i < max_row; i++)
{
for (int j = 0; j < max_col; j++)
{
cout<>matrix.elem_num;
cout<<"input the number of row:";
cin>>matrix.row_num;
cout<<"input the number of column:";
cin>>matrix.col_num;
cout<<"input the triple of matrix(下标从1开始):"<>matrix.data[i].row>>matrix.data[i].col>>matrix.data[i].elem;
}
cout<<"input finished!"<