本文要点:

1.对称矩阵与稀疏矩阵

2.两种矩阵的压缩存储

3.代码实现两种矩阵


对称矩阵<SymmetryMatrix>

1.对称矩阵也是一种特殊矩阵,满足Aij = Aji(设矩阵为A,且有0<=i<N-1 && 0<=j<N-1),这种矩阵以对角线分割为上三角和下三角,关于对角线对称的元素相等。

2.对称矩阵的压缩存储

如果把矩阵中的每个元素都存储起来,那么就会显得浪费空间,因为每两个关于对角线对称的元素相等,因此就可以将矩阵压缩存储到一个数组Array中,即:将对称的两个元素存一份,对角线上的元素都存储起来,也就是说只存储上三角或下三角,那么存储的元素的总个数就为n(n+1)/2个,这样,当n特别大的时候,也就最有效。

压缩存储的数组与矩阵之间满足:Array[i*(i+1)/2+j] = Martix[i][j](下三角存储i>=j)

3.代码实现

template<class T>
class SymmetryMatrix
{
public:
	SymmetryMatrix(const T* a,size_t n)
		:_matrix(new T[n*(n+1)/2])
		,_size(n*(n+1)/2)
		,_n(n)
	{
		size_t index = 0;	//表示一维数组的下标
		for (size_t i=0; i<n; ++i)
		{
			for (size_t j=0; j<n; ++j)
			{
				if (i >= j)		//存储下三角
				{
					_matrix[index++] = a[i*n+j];
					//index++;
				}
				else
					continue;
			}
		}
	}

	T& Access(size_t row,size_t col)	//访问数据
	{
		if (row < col)
		{
			std::swap(row,col);
		}
		return _matrix[row*(row+1)/2+col];
	}

	void Display()
	{
		for (size_t i=0; i<_n; i++)
		{
			for (size_t j=0; j<_n; j++)
			{
				cout<<Access(i,j)<<" ";
			}
			cout<<endl;
		}
	}
protected:
	T* _matrix;		//压缩存储的一维数组
	size_t _size;	//可存储的大小
	size_t _n;		//行列的大小		
};

稀疏矩阵<SparseMatrix>

1.稀疏矩阵中,有效数据(非0)的数量远小于非法数据的个数(0为非法数据)

2.稀疏矩阵的存储也是压缩存储的,但与对称矩阵不同的地方有两点:

(1)稀疏矩阵的压缩存储只存储有效值;

(2)稀疏矩阵存储以行优先的顺序存储在三元组中,表示为:{row,col,value},row和col分别表示该有效值的行和列。


一、存储及输出

存储的方法类似于对称矩阵的存储,但要将行和列也进行存储;

输出的时候多考虑表示数组的下标会不会越界

	//数据的存储
	SpareMatrix(const T* a,size_t row,size_t col,const T& invalid)
		:_rowSize(row)
		,_colSize(col)
		,_invalid(invalid)
	{
		for(size_t i=0; i<row; i++)
		{
			for (size_t j=0; j<col; j++)
			{
				if (a[i*col+j] != invalid)	//有效值
				{
					Triple<T> t(i,j,a[i*col+j]);
					t._value = a[i*col+j];
					t._row = i;
					t._col = j;
					_martix.push_back(t);
				}
			}
		}
	}
	//输出
	void Display()
	{
		size_t index = 0;
		for (size_t i=0; i<_rowSize; ++i)
		{
			for (size_t j=0; j<_colSize; ++j)
			{
				if (index < _martix.size()
					&& i == _martix[index]._row
					&& j == _martix[index]._col)
				{
					cout<<_martix[index]._value<<" ";
					++index;
				}
				else
				{
					cout<<0<<" ";
				}
			}
			cout<<endl;
		}
		cout<<endl;
	}


二、一般转置算法


从上图我们可以看出:矩阵转置后,三元组中的顺序也发生了改变。因此要得到矩阵M转置后的矩阵TM,我们只需将三元组的顺序改变就好了。转置的三步:

a.将矩阵的行列交换

b.将每个三元组中的行、列交换

c.将原矩阵三元组的顺序重排得到新的三元组

	SpareMatrix<T> Transport()	//矩阵的转置
	{
		SpareMatrix<T> TSMartix;
		TSMartix._rowSize = _colSize;//交换行列
		TSMartix._colSize = _rowSize;
		TSMartix._invalid = _invalid;
		TSMartix._martix.reserve(_martix.size());

		for (size_t col=0; col<_colSize; ++col)	//以列优先进行查找
		{
			for (size_t index=0; index<_martix.size(); ++index)
			{
				if (_martix[index]._col == col)
				{
					Triple<T> tmp(_martix[index]._col,_martix[index]._row,_martix[index]._value);
					TSMartix._martix.push_back(tmp);
				}
			}
		}
		return TSMartix;
	}

这样算法即使可以得到结果,但是效率高不高呢?!算算时间复杂度--------

O(矩阵的列数*有效数值的个数),如果这个矩阵是500*500呢?!显然效率就会特别低了。因此,我们就需要一种转置算法来提高效率。


三、快速转置

(假设原矩阵为M,转置后得到的矩阵为FSTM)

我们按照普通转置的算法,将M的三元组的次序进行转置,并将每次转置后的数据能直接放到FSTM三元组正确的位置。但这种算法的前提是:需要预先知道M每行的有效值个数,将M中第一个有效数放到正确位置后,并记下该位置,以后对M的三元组中的每个数进行转置时,都可以直接放到正确的位置。

引入两个量count和start,count-----记录每行的有效数据的个数

start-------表示每行第一个数的起始位置


分析上图我们可以得到:start = 上一行的起始位置 + 上一行的有效数个数

时间复杂度:O(2*有效值的个数+列数)

代码实现:

	SpareMatrix<T> FastTransport()
	{
		SpareMatrix<T> FSTMartix;
		FSTMartix._rowSize = _colSize;//交换行列
		FSTMartix._colSize = _rowSize;
		FSTMartix._invalid = _invalid;
		FSTMartix._martix.resize(_martix.size());

		int* count = new int[_colSize];
		memset(count,0,_martix.size()*sizeof(int));
		int* start = new int[_colSize];
		memset(start,0,_martix.size()*sizeof(int));

		//统计每列的有效值个数
		for (size_t i=0; i<_martix.size(); ++i)
		{
			count[_martix[i]._col]++;
		}

		//计算每一列的起始位置
		for (size_t j=1; j<_colSize; ++j)
		{
			start[j] = start[j-1] + count[j-1];//每一列起始位置=上一列起始位置+上一列有效数个数
		}

		//转置
		for (size_t index=0; index<_martix.size(); ++index)
		{
			size_t tmp = _martix[index]._col;	//取到原三元组中每个数的列
			FSTMartix._martix[start[tmp]]._col = _martix[index]._row;
			FSTMartix._martix[start[tmp]]._row = _martix[index]._col;
			FSTMartix._martix[start[tmp]]._value = _martix[index]._value;
			++start[tmp];	//start指针要向后移位
		}
		return FSTMartix;
	}



稀疏矩阵代码的整体实现:

template<class T>
struct Triple
{
	Triple(size_t row,size_t col,const T& value = T())
		:_row(row)
		,_col(col)
		,_value(value)
	{}
	Triple()
	{}

	size_t _row;	//行
	size_t _col;	//列
	T _value;		//有效值
};

template<class T>
class SpareMatrix
{
public:
	SpareMatrix()
		:_martix(NULL)
		,_rowSize(0)
		,_colSize(0)
		,_invalid(T())
	{}
	//数据的存储
	SpareMatrix(const T* a,size_t row,size_t col,const T& invalid)
		:_rowSize(row)
		,_colSize(col)
		,_invalid(invalid)
	{
		for(size_t i=0; i<row; i++)
		{
			for (size_t j=0; j<col; j++)
			{
				if (a[i*col+j] != invalid)	//有效值
				{
					Triple<T> t(i,j,a[i*col+j]);
					t._value = a[i*col+j];
					t._row = i;
					t._col = j;
					_martix.push_back(t);
				}
			}
		}
	}
	//输出
	void Display()
	{
		size_t index = 0;
		for (size_t i=0; i<_rowSize; ++i)
		{
			for (size_t j=0; j<_colSize; ++j)
			{
				if (index < _martix.size()
					&& i == _martix[index]._row
					&& j == _martix[index]._col)
				{
					cout<<_martix[index]._value<<" ";
					++index;
				}
				else
				{
					cout<<0<<" ";
				}
			}
			cout<<endl;
		}
		cout<<endl;
	}
	SpareMatrix<T> Transport()	//矩阵的转置
	{
		SpareMatrix<T> TSMartix;
		TSMartix._rowSize = _colSize;//交换行列
		TSMartix._colSize = _rowSize;
		TSMartix._invalid = _invalid;
		TSMartix._martix.reserve(_martix.size());

		for (size_t col=0; col<_colSize; ++col)	//以列优先进行查找
		{
			for (size_t index=0; index<_martix.size(); ++index)
			{
				if (_martix[index]._col == col)		
				{
					Triple<T> tmp(_martix[index]._col,_martix[index]._row,_martix[index]._value);
					TSMartix._martix.push_back(tmp);
				}
			}
		}
		return TSMartix;
	}

	SpareMatrix<T> FastTransport()
	{
		SpareMatrix<T> FSTMartix;
		FSTMartix._rowSize = _colSize;//交换行列
		FSTMartix._colSize = _rowSize;
		FSTMartix._invalid = _invalid;
		FSTMartix._martix.resize(_martix.size());

		int* count = new int[_colSize];
		memset(count,0,_martix.size()*sizeof(int));
		int* start = new int[_colSize];
		memset(start,0,_martix.size()*sizeof(int));

		//统计每列的有效值个数
		for (size_t i=0; i<_martix.size(); ++i)
		{
			count[_martix[i]._col]++;
		}

		//计算每一列的起始位置
		for (size_t j=1; j<_colSize; ++j)
		{
			start[j] = start[j-1] + count[j-1];//每一列起始位置=上一列起始位置+上一列有效数个数
		}

		//转置
		for (size_t index=0; index<_martix.size(); ++index)
		{
			size_t tmp = _martix[index]._col;	//取到原三元组中每个数的列
			FSTMartix._martix[start[tmp]]._col = _martix[index]._row;
			FSTMartix._martix[start[tmp]]._row = _martix[index]._col;
			FSTMartix._martix[start[tmp]]._value = _martix[index]._value;
			++start[tmp];	//start指针要向后移位
		}
		return FSTMartix;
	}
protected:
	vector<Triple<T>> _martix;
	size_t _rowSize;
	size_t _colSize;
	T _invalid;	//非法值
};



本文转载:CSDN博客