稀疏矩阵的转置算法

关于数组,大家并不陌生,几乎所有的程序设计语言都把数组类型设为了固有类型。
数组是n(n>1)个相同类型数据元素构成的有限序列,它是下标-值偶对的集合,即集合中的每一个元素都是由一个值和一组下标组成的。
数组一般分为一维、二维、三维数组,其中二维数组也称为矩阵,本文主要讲解对于矩阵中稀疏矩阵的相关操作算法。

稀疏矩阵

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

例如:
在这里插入图片描述
对于稀疏矩阵,我们通常采用三元组的形式来进行压缩,不考虑0元素的个数和位置,只记录非0元素的行列位置和数据值。
三元组:(行号,列号,值)

上图的稀疏矩阵的三元组表示为:
在这里插入图片描述

假设以顺序存储结构来存储三元组表,则可以得到稀疏矩阵的一种压缩方式——三元组顺序表。

三元组顺序表

三元组顺序表的相关定义
#include<iostream>
using namespace std;
#define maxsize 1000     //最大存储容量
#define OK 1
#define FALSE 0
typedef int ElemType;
typedef int status;
typedef struct {
	int i, j;   //行列号
	ElemType e;  //值
}Triple;    //三元组(行号,列号,值)

typedef struct {
	Triple data[maxsize + 1];
	int mu, nu, tu;  //稀疏矩阵的行数,列数,非零元个数
}TSMatrix;   //稀疏矩阵

故此时稀疏矩阵的图示为:
稀疏矩阵的图示

稀疏矩阵的输入

首先输入一个稀疏矩阵,然后将其压缩成三元组表。

void CreateMatrix(TSMatrix  &M)
{
	int A[100][100];      
	int a, b;        //a,b用来存储矩阵的行号列号        
	int h, l;
	int m=0;         //m为非零元个数计数器
	cout << "请输入矩阵的行号、列号:";
	cin >> a >> b;
	for (h = 1; h <= a; h++)
		for (l = 1; l <= b; l++)
		{
			cin >> A[h][l];
		}
	cout << endl;
	for (h = 1; h <= a; h++)         //将稀疏矩阵的非零元个数表示为三元组表
	{
		for (l = 1; l <= b; l++)
		{
			if (A[h][l] != 0)
			{
				m++;
				M.data[m].i = h;
				M.data[m].j = l;
				M.data[m].e = A[h][l];
			}
		}
		M.tu = m;
		M.mu = a;
		M.nu = b;
	}
}

稀疏矩阵的输出

以三元组表的形式输出非零元素
void PrintTriple(TSMatrix M)
{ // 输出稀疏矩阵M的三元组表示
	int i;
	cout << M.mu << "行" << M.nu << "列" << M.tu << "个非零元素" << endl << endl;
	cout << "行" << " 列" << " 元素值" << endl;
	for (i = 1; i <= M.tu; i++)
		cout << M.data[i].i << "  " << M.data[i].j << "    " << M.data[i].e << endl ;
	cout << endl;
}
输出稀疏矩阵

将有三元组表中对应位置的元素赋值,其余的均赋值为0

void PrintMatrix(TSMatrix M)
{  //输出稀疏矩阵M
	int A[100][100];
	int a, b,k=1;     //k为三元组表中元素个数的计数器
	for (a = 1; a <= M.mu; a++)   //将三元组表转化为稀疏矩阵
	{
		for (b = 1; b <= M.nu; b++)
		{
			if (a == M.data[k].i && b == M.data[k].j)
			{
				A[a][b] = M.data[k].e;
				k++;
			}
			else
			{
				A[a][b] = 0;
			}
		}
	}
	cout << "稀疏矩阵为:" << endl;
	for (a = 1; a <= M.mu; a++)
	{
		for (b = 1; b <= M.nu; b++)
		cout << A[a][b] << " ";
		cout << endl;
	}
}

稀疏矩阵的转置算法

转置运算
status TransposeMatrix(TSMatrix M,TSMatrix &T)
{  //稀疏矩阵的转置
	int p, q,col;
	ElemType e;
//q代表转置后矩阵的元素计数器,p代表原矩阵中元素的计数器,e代表元素值,col代表行号顺序
	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;
}
快速转置

由于上面算法的时间复杂度太高,为O(M.nu*M.mu),故将算法改进,降低时间复杂度。
思路如下:

  1. 求原矩阵中每一行的三元组个数
  2. 求原矩阵中的每一行中第一个三元组在转置后矩阵中的序号
  3. 讲原矩阵中每个三元组根据序号依次转换到转置后矩阵的相应位置

故在原矩阵中增加了

  1. 数组num来记录原矩阵中每列的非零元素个数(即转置后矩阵中每行的非零元个数)
  2. 数组pos来记录转置后矩阵中每行第一个元素的位置
status TransposeMatrix_plus(TSMatrix M, TSMatrix& T)
{   //稀疏矩阵转置的改进版,快速转置
//在原矩阵中增加了num[j]来记录原矩阵M中每列的非零元素个数,即转置后T中每行的非零元个数
	//增加了pos[j]来记录转置后矩阵T中每行第一个元素的位置
	int i, j, k;
	int* num = (int*)malloc(M.nu * sizeof(int));
	int* pos = (int*)malloc(M.nu * sizeof(int));
	T.mu = M.nu;
	T.nu = M.mu;
	T.tu = M.tu;
	if (M.tu)
	{
		//第一步:确定M中每一列的num值
		for (j = 1; j <= M.nu; j++)
		{
			num[j] = 0;   //将M中的每一列(T中的每一行)的num先设定为零
		}
		for (k = 1; k <= M.tu; k++)
		{
		 num[M.data[k].j]++;    //遍历整个M矩阵,统计M中每一列(T中的每一行)元素个数num
		}
		//第二步:确定T中各行元素的起始位置pos值

		for (j = 2; j <= M.nu; j++)
		{
			pos[1] = 1;
			pos[j] = pos[j - 1] + num[j - 1];
		}
		//第三步:依次转置矩阵
		for (k = 1; k <= M.tu; k++)
		{
			j = M.data[k].j;
			T.data[pos[j]].j = M.data[k].i;
			T.data[pos[j]].i = M.data[k].j;
			T.data[pos[j]].e = M.data[k].e;
			pos[j]++;
		}
	}
	return OK;
}

随意编写了个主函数来进行测试,转置用的是快速转置算法

void main()
{
	TSMatrix A, B;
	CreateMatrix(A);
	PrintTriple(A);
	TransposeMatrix_plus(A, B);
	cout << "转置后" << endl;
	PrintMatrix(B);
}

运行结果如下:
在这里插入图片描述

  • 走得多慢都无所谓,只要你不停下脚步。

仪式感过后,人烟消散,最委屈的还是主角。

  • 16
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值