对称矩阵&稀疏矩阵的两种转置算法

SymmentryMatrix(对称矩阵)

    对称矩阵和对称矩阵的压缩存储>

 设一个N*N的方阵Array,Array中任意元素Arrayij,当且仅当Arrayij == Arrayji(0 <= i <= N-1 && 0 <= j <= N-1),Array是对称矩阵。以矩阵的对角线为分隔,分为上三角和下三角.

 我们发现如果将该对称矩阵的每个元素都存入则会存在内存浪费问题,那仫如何解决? 这就提到了对称矩阵的压缩存储了--则只需要存储上三角或者下三角的元素即可。

     注意:

     (1).在对称矩阵的压缩存储中因为只存储上三角或者下三角则每一行的有效数据为:0,1,2,3,4(以5*5的方阵为例),那仫根据等差数列的特性最多存储n*(n+1)/2个元素

     (2).在输出压缩存储的对称方阵的时候,假设存储的是下三角,如果该下标属于下三角(i >= j)则直接输出,如果要输出的下标属于上三角(i < j)则需要将该上三角的行列交换.

      


//对称矩阵的存储
template<typename T>
class SymMatrix
{
public:
	SymMatrix(T *matrix,size_t n)
		:_matrix(new T[n*(n+1)/2])
		,_size(n*(n+1)/2)
		,_n(n)
	{
		for(size_t i=0;i<n;++i)   //存储下三角
		{
			for(size_t j=0;j<n;++j)
			{
				if (i >= j)
				{
					_matrix[i*(i+1)/2+j]=matrix[i*n+j];
				}
			}
		}
	}
	T& Access(size_t i,size_t j)
	{
		if(i < j)    //如果是上三角元素
		{
			swap(i,j);
		}
		return _matrix[i*(i+1)/2+j];
	}
	void PrintSymMatrix()
	{
		for (size_t i=0;i<_n;++i)
		{
			for(size_t j=0;j<_n;++j)
			{
				cout<<Access(i,j)<<" ";
			}
			cout<<endl;
		}
		cout<<endl;
	}
protected:
	T* _matrix;
	size_t _size;
	size_t _n;
};



 

 SpareMatrix(稀疏矩阵)

     稀疏矩阵的三元组存储和稀疏矩阵的两种转置算法>

     M*N的矩阵,矩阵中有效值的个数远远小于无效值的个数,且有效值的分布没有规律,这就是稀疏矩阵.如何高效的去存储一个稀疏矩阵呢?要想高效的存储一个稀疏矩阵则只需要存储该矩阵的有效值即可,但是存在一个问题就是如何找到该有效值的位置呢?直接将该有效值的行,列坐标一起存储起来不就可以了吗?这就是稀疏矩阵的三元组存储形式.且值得注意的是该矩阵是以行优先的方法存储的.

    稀疏矩阵的两种转置算法>

    一.普通的转置算法

       

       普通转置算法的时间复杂度为O(原来稀疏矩阵的列*有效值的个数).

       

	SpareMatrix<T> Transport()   //矩阵的转置--普通算法
	{
		SpareMatrix<T> tsm;
		tsm._row=_col;
		tsm._col=_row;
		tsm._v.reserve(_v.size());
		for (size_t i=0;i<_col;++i)
		{
			//i控制新矩阵的行也就是旧矩阵的列
			size_t index=0;
			while (index < _v.size())
			{
				//index控制原来三元组的元素个数
				if (_v[index]._cols == i)
				{
					//遍历原来的三元组如果存在满足对应下标的元素则进入新的三元组.
					Triple<T> tmp(_v[index]._cols,_v[index]._rows,_v[index]._value);
					tsm._v.push_back(tmp);
				}
				++index;
			}
		}
		return tsm;
	}



 

    

     二.快速转置算法>

      在之前普通的转置算法中,将原来的三元组遍历了N(原来三元组的列)遍,效率不高,这就提到了快速转置.

      (1).如果我们可以统计出转置之后的矩阵每一行有效值的个数,并找到转置之后每一行第一个元素在新三元组的存储位置,此时再遍历原来的三元组将有效数据直接放入新三元组的对应位置处,不是更加高效吗?此时的时间复杂度为O(2*有效值的个数+N)

      (2).CountNum统计新矩阵每一行的有效值的个数等价于统计原来矩阵的每一列的有效值的个数,这里就有点哈希表的思想了,通过遍历原来的三元组,将原来三元组的列下标作为CountNum的下标,只要原来三元组的列存在有效值该CountNum的对应下标就+1.

      (3).StartPos找到新矩阵的每一行的第一个元素在新三元组的存储下标

      (4).此时再次遍历原来的三元组,将该数据直接放入新三元组对应的下标处,此时将StartPos位置的存储数据+1,防止插入到相同的位置处.

     

 

      

	SpareMatrix<T> FastTransport()     //矩阵的转置--快速算法
	{
		SpareMatrix<T> ftsm;
		ftsm._row=_col;
		ftsm._col=_row;
		ftsm._v.resize(_v.size());
		//新矩阵的行就是旧矩阵的列
		//CountNum统计新矩阵每一行有效值的个数
		int *CountNum=new int[_col];
		memset(CountNum,0,sizeof(int)*_col);
		for (size_t i=0;i<_v.size();++i)
		{
			int col=_v[i]._cols;
			++CountNum[col];
		}
		//StartPos记录新矩阵的每一行第一个元素在三元组的存储下标
		int *StartPos=new int[_col];
		memset(StartPos,0,sizeof(int)*_col);
		size_t i=0;
		StartPos[i]=0;
		for (i=1;i<_col;++i)
		{
			StartPos[i]=StartPos[i-1]+CountNum[i-1];
		}
		//遍历原来的三元组,找到数据就直接放入新三元组的对应的下标处
		for (size_t i=0;i<_v.size();++i)
		{
			int col=_v[i]._cols;
			size_t tmp=StartPos[col];
			ftsm._v[tmp]._rows=_v[i]._cols;
			ftsm._v[tmp]._cols=_v[i]._rows;
			ftsm._v[tmp]._value=_v[i]._value;
			++StartPos[col];
		}
		delete[]CountNum;
		delete[]StartPos;
		return ftsm;
	}



 

   

     程序源码>

     Matrix.h

        

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <iostream>
#include<vector>
using namespace std;

//对称矩阵的存储
template<typename T>
class SymMatrix
{
public:
	SymMatrix(T *matrix,size_t n)
		:_matrix(new T[n*(n+1)/2])
		,_size(n*(n+1)/2)
		,_n(n)
	{
		for(size_t i=0;i<n;++i)   //存储下三角
		{
			for(size_t j=0;j<n;++j)
			{
				if (i >= j)
				{
					_matrix[i*(i+1)/2+j]=matrix[i*n+j];
				}
			}
		}
	}
	T& Access(size_t i,size_t j)
	{
		if(i < j)    //如果是上三角元素
		{
			swap(i,j);
		}
		return _matrix[i*(i+1)/2+j];
	}
	void PrintSymMatrix()
	{
		for (size_t i=0;i<_n;++i)
		{
			for(size_t j=0;j<_n;++j)
			{
				cout<<Access(i,j)<<" ";
			}
			cout<<endl;
		}
		cout<<endl;
	}
protected:
	T* _matrix;
	size_t _size;
	size_t _n;
};

//稀疏矩阵的压缩存储
template<class T>
struct Triple
{
	Triple(size_t rows=0,size_t cols=0,const T& x=T())
		:_value(x)
		,_rows(rows)
		,_cols(cols)
	{}
	T _value;
	size_t _rows;   //行
	size_t _cols;   //列
};

template<class T>
class SpareMatrix
{
public:
	SpareMatrix()
		:_row(0)
		,_col(0)
		,_invalid(T())
		,_v(NULL)
	{}
	SpareMatrix(T* a,size_t m,size_t n,const T& invalid)
		:_row(m)
		,_col(n)
		,_invalid(invalid)
	{
		for(size_t i=0;i<m;++i)
		{
			for(size_t j=0;j<n;++j)
			{
				if (a[i*n+j] != invalid)
				{
					Triple<T> t(i,j,a[i*n+j]);
					_v.push_back(t);
				}
			}
		}
	}
	SpareMatrix<T> Transport()   //矩阵的转置--普通算法
	{
		SpareMatrix<T> tsm;
		tsm._row=_col;
		tsm._col=_row;
		tsm._v.reserve(_v.size());
		for (size_t i=0;i<_col;++i)
		{
			//i控制新矩阵的行也就是旧矩阵的列
			size_t index=0;
			while (index < _v.size())
			{
				//index控制原来三元组的元素个数
				if (_v[index]._cols == i)
				{
					//遍历原来的三元组如果存在满足对应下标的元素则进入新的三元组.
					Triple<T> tmp(_v[index]._cols,_v[index]._rows,_v[index]._value);
					tsm._v.push_back(tmp);
				}
				++index;
			}
		}
		return tsm;
	}
	SpareMatrix<T> FastTransport()     //矩阵的转置--快速算法
	{
		SpareMatrix<T> ftsm;
		ftsm._row=_col;
		ftsm._col=_row;
		ftsm._v.resize(_v.size());
		//新矩阵的行就是旧矩阵的列
		//CountNum统计新矩阵每一行有效值的个数
		int *CountNum=new int[_col];
		memset(CountNum,0,sizeof(int)*_col);
		for (size_t i=0;i<_v.size();++i)
		{
			int col=_v[i]._cols;
			++CountNum[col];
		}
		//StartPos记录新矩阵的每一行第一个元素在三元组的存储下标
		int *StartPos=new int[_col];
		memset(StartPos,0,sizeof(int)*_col);
		size_t i=0;
		StartPos[i]=0;
		for (i=1;i<_col;++i)
		{
			StartPos[i]=StartPos[i-1]+CountNum[i-1];
		}
		//遍历原来的三元组,找到数据就直接放入新三元组的对应的下标处
		for (size_t i=0;i<_v.size();++i)
		{
			int col=_v[i]._cols;
			size_t tmp=StartPos[col];
			ftsm._v[tmp]._rows=_v[i]._cols;
			ftsm._v[tmp]._cols=_v[i]._rows;
			ftsm._v[tmp]._value=_v[i]._value;
			++StartPos[col];
		}
		delete[]CountNum;
		delete[]StartPos;
		return ftsm;
	}
	void PrintSpareMatrix()
	{
		size_t index=0;
		for(size_t i=0;i<_row;++i)
		{
			for(size_t j=0;j<_col;++j)
			{
				if((index < _v.size())
					&&(_v[index]._rows == i)
					&&(_v[index]._cols == j))
				{
					cout<<_v[index]._value<<" ";
					index++;
				}
				else
				{
					cout<<_invalid<<" ";
				}
			}
			cout<<endl;
		}
		cout<<endl;
	}
protected:
	vector<Triple<T>> _v;
	size_t _row;    //行
	size_t _col;    //列
	T _invalid;
};


 

 

     test.cpp

     

#define _CRT_SECURE_NO_WARNINGS 1
#include "Matrix.h"

void testSymMatrix()
{
	int arr[5][5]=
	{
		{0,1,2,3,4},
		{1,0,1,2,3},
		{2,1,0,1,2},
		{3,2,1,0,1},
		{4,3,1,2,0}
	};
	SymMatrix<int> sm((int *)arr,5);
	sm.PrintSymMatrix();
}
void testSpareMatrix()
{
	int arr[6][5]=
	{
		{1,0,3,0,5},
		{0,0,0,0,0},
		{0,0,0,0,0},
		{1,0,3,0,5},
		{0,0,0,0,0},
		{0,0,0,0,0}
	};
	SpareMatrix<int> sm1((int *)arr,6,5,0);
	sm1.PrintSpareMatrix();

	SpareMatrix<int> sm2;
	sm2=sm1.Transport();
	sm2.PrintSpareMatrix();

	SpareMatrix<int> sm3;
	sm3=sm1.FastTransport();
	sm3.PrintSpareMatrix();
}
int main()
{
	//testSymMatrix();
	testSpareMatrix();
	system("pause");
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值