1. 对称矩阵及对称矩阵的压缩存储

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

  压缩存储称矩阵存储时只需要存储上三角/下三角的数据,所以最多存储n(n+1)/2个数据。

对称矩阵和压缩存储的对应关系:下三角存储i>=j,SymmetricMatrix[i][j] == Array[i*(i+1)/2+j]。

spacer.gif 实现代码如下:

  

#include <iostream>
using namespace std;

template <class T>
class SymmetricMatrix
{
public:
	SymmetricMatrix(T * a, size_t size)
		:_a(new T[size*(size+1)/2])
		,_size(size*(size + 1) / 2)
		,_n(size)
	{
		size_t index = 0;
		for (size_t i = 0; i < size; i++)
		{
			for (size_t j = 0; j < size; j++)
			{
				if (i >= j)
				{
					_a[index++] = a[size*i + j];//按照一维数组的方式存入数据
				}
				else
				{
					break;
				}
			}
		}
	}
	~SymmetricMatrix()
	{
		if (_a)
		{
			delete[]_a;
			_a = NULL;
			_size = 0;
		}
	}
	T& Access(size_t i, size_t j)
	{
		if (i < j)
		{
			swap(i, j);//当是上三角时,交换i和j
		}
		return _a[i*(i + 1) / 2 + j];
	}
	void display()
	{
		for (size_t i = 0; i < _n; i++)
		{
			for (size_t j = 0; j < _n; j++)
			{
				if (i >= j)
				{
					cout << _a[i*(i + 1) / 2 + j] << " ";
				}
				else
				{
					cout << _a[j*(j + 1) / 2 + i] << " ";
				}
			}
			cout << endl;
		}
	}
protected:
	size_t _size;//存储数据的个数
	T * _a;//存储之后的对称矩阵的指针
	size_t _n;//对称矩阵的大小
};

2.稀疏矩阵

 M*N的矩阵,矩阵中有效值的个数远小于无效值的个数,且这些数据的分布没有规律

 比如下面这个矩阵:

 wKioL1cUekXTHIOVAAAJWXulRyo033.png

spacer.gif (1)稀疏矩阵的压缩存储

   压缩存储只存储极少数的有效数据。使用{row,col,value}三元组存储每一个有效数据,三元组按原矩阵中的位置,以行优先级先后顺序依次存放。

  那么上面的矩阵压缩存储结果就是:

  wKiom1cUefrgD4A3AAAEUJora9M325.png

  三元组的定义:

   

template<typename T>
struct Triple//三元组
{
	Triple<T>::Triple()//无参的构造函数
	{

	}

	Triple(size_t row, size_t col, T value)//构造函数
		:_row(row)
		,_col(col)
		,_value(value)
	{

	}

	size_t _row;//行
	size_t _col;//列
	T _value;//值

};

 压缩存储的实现代码:

template<typename T>
class SparseMatrix
{
public:

	SparseMatrix()
	{

	}
        //用vector顺序表来存储三元组的信息 
	SparseMatrix(T * a, size_t m, size_t n, const T & invalid)
		:_rowsize(m)
		,_colsize(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> cur(i, j, a[i*n + j]);
					_a.push_back(cur);
				}
			}
		}
	}
protected:
	vector <Triple<T>> _a;
	size_t _rowsize;
	size_t _colsize;
	T _invalid;
};

 (2)稀疏矩阵的转置

   将原矩阵的行、列对换,也就是将[i][j][j][i]位置上的数据对换。

 wKioL1cUfJXBya7rAAAW17qk6v4073.png

  普通转置(列转置)

    按照原矩阵的列优先把vector中的三元组放入新的容器中,并且交换行和列的值

	SparseMatrix<T> Transport()
	{
		assert(_a.size() < 0);
		//创建新的矩阵,交换行列的值
		SparseMatrix<T> ret;
		ret._rowsize = _colsize;
		ret._colsize = _rowsize;
		ret._invalid = _invalid;
		//两次循环
		for (size_t i = 0; i < _colsize; i++)//按原矩阵的列扫描
		{
			size_t index = 0;
			while (index < _a.size())
			{
				if (_a[index]._col == i)
					//如果三元组中的列值=i时
					//交换行列的值,放入新的矩阵
				{
					Triple<T> tmp(_a[index]._col, _a[index]._row, _a[index]._value);
					ret._a.push_back(tmp);
				}
				index++;
			}
			if (_a.size() == ret._a.size())
			{
				break;//两个容器的大小相同时,break
			}
		}
		return ret;
	}
	

  用此方法可以有效的转置矩阵,我们来看一下此函数的时间复杂度:O(col * _a.size())——矩阵的列*矩阵的元素总和。

   如果元素很多就会浪费很多的时间。有没有办法让两层循环变成一层循环呢?付出空间上的代价,换取时间效率。

  快速转置

  我们只用一层循环来遍历容器_a中所有元素,并把该元素放到指定的位置。这样我们就需要一个数组rowStar来存放第i个元素所在位置。在定义这个数组之前,我们还需要一个数组rowCount来实现统计矩阵第i行元素的数量。这样我们才能更方便的知道第i个元素应该存放的位置。

       wKioL1cUgh7xfJ6XAAAl71YfXUA333.png实现代码如下:

SparseMatrix<T> FastTransport()
	{
		assert(_a.size() > 0);
		SparseMatrix<T> ret;

		ret._rowsize = _colsize;//行列值互换
		ret._colsize = _rowsize;
		ret._invalid = _invalid;

		int * rowCount = new int[_colsize];
		int * rowStart = new int[_colsize];
		//初始化rowCount和rowStart为0
		memset(rowCount, 0, sizeof(int)* _colsize);
		memset(rowStart, 0, sizeof(int) * _colsize);
		//初始化
		size_t index = 0;
		while (index < _a.size())
		{
			rowCount[_a[index]._col]++;
			++index;
		}
		rowStart[0] = 0;
		for (size_t i = 1; i < _colsize; i++)
		{
			rowStart[i] = rowStart[i - 1] + rowCount[i - 1];
		}

		ret._a.resize(_a.size());//复制顺序表_a,容量相同
		index = 0;
		Triple<T> tmp;
		while (index < _a.size())
		{
			size_t rowIndex = _a[index]._col;//行数
			size_t row = rowStart[rowIndex];//当前行的起始位置

			//交换行和列
			tmp._col = _a[index]._row;
			tmp._row = _a[index]._col;
			tmp._value = _a[index]._value;

			ret._a[row] = tmp;//将tmp放入ret计算好的位置
			rowStart[row]++;
			index++;
		}
		delete[] rowCount;
		delete[] rowStart;
		return ret;
	}

此函数的时间复杂度为O(col + _a.size());和普通转置相比,效率提高了很多。

最后,附上完整代码(稀疏矩阵):

#pragma once

#include <iostream>
#include <vector>
#include <assert.h>
using namespace std;

template<typename T>
struct Triple//三元组
{
	Triple<T>::Triple()//无参的构造函数
	{

	}

	Triple(size_t row, size_t col, T value)
		:_row(row)
		,_col(col)
		,_value(value)
	{

	}

	size_t _row;//行
	size_t _col;//列
	T _value;//值

};

template<typename T>
class SparseMatrix
{
public:

	SparseMatrix()//无参的构造函数
	{

	}
        //用vector顺序表来存储三元组的信息
	SparseMatrix(T * a, size_t m, size_t n, const T & invalid)
		:_rowsize(m)
		,_colsize(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> cur(i, j, a[i*n + j]);
					_a.push_back(cur);
				}
			}
		}
	}

	void Display()//打印矩阵
	{
		size_t index = 0;
		for (size_t i = 0; i < _rowsize; i++)
		{
			for (size_t j = 0; j < _colsize; j++)
			{
				if ((index < _a.size()) && _a[index]._row == i&&_a[index]._col == j)
				{
					cout << _a[index]._value << " ";
					index++;
				}
				else
				{
					cout << _invalid << " ";
				}
			}
			cout << endl;
		}	
		cout << endl;
	}

	//普通转置
	SparseMatrix<T> Transport()
	{
		assert(_a.size() 》 0);
		//创建新的矩阵,交换行列的值
		SparseMatrix<T> ret;
		ret._rowsize = _colsize;
		ret._colsize = _rowsize;
		ret._invalid = _invalid;
		//两次循环
		for (size_t i = 0; i < _colsize; i++)//按原矩阵的列扫描
		{
			size_t index = 0;
			while (index < _a.size())
			{
				if (_a[index]._col == i)
					//如果三元组中的列值=i时
					//交换行列的值,放入新的矩阵
				{
					Triple<T> tmp(_a[index]._col, _a[index]._row, _a[index]._value);
					ret._a.push_back(tmp);
				}
				index++;
			}
			if (_a.size() == ret._a.size())
			{
				break;//两个容器的大小相同时,break
			}
		}
		return ret;
	}

	//快速转置
	SparseMatrix<T> FastTransport()
	{
		assert(_a.size() > 0);
		SparseMatrix<T> ret;

		ret._rowsize = _colsize;//行列值互换
		ret._colsize = _rowsize;
		ret._invalid = _invalid;

		int * rowCount = new int[_colsize];
		int * rowStart = new int[_colsize];
		//初始化rowCount和rowStart为0
		memset(rowCount, 0, sizeof(int)* _colsize);
		memset(rowStart, 0, sizeof(int) * _colsize);
		//初始化
		size_t index = 0;
		while (index < _a.size())
		{
			rowCount[_a[index]._col]++;
			++index;
		}
		rowStart[0] = 0;
		for (size_t i = 1; i < _colsize; i++)
		{
			rowStart[i] = rowStart[i - 1] + rowCount[i - 1];
		}

		ret._a.resize(_a.size());//复制顺序表_a,容量相同
		index = 0;
		Triple<T> tmp;
		while (index < _a.size())
		{
			size_t rowIndex = _a[index]._col;//行数
			size_t row = rowStart[rowIndex];//当前行的起始位置

			//交换行和列
			tmp._col = _a[index]._row;
			tmp._row = _a[index]._col;
			tmp._value = _a[index]._value;

			ret._a[row] = tmp;//将tmp放入ret计算好的位置
			rowStart[row]++;
			index++;
		}
		delete[] rowCount;
		delete[] rowStart;
		return ret;
	}

protected:
	vector <Triple<T>> _a;//容器
	size_t _rowsize;//行
	size_t _colsize;//列
	T _invalid;//非法值
};


void Test()
{
	int array[5][4] =
	{
		{ 1, 0, 3, 0, },
		{ 0, 0, 0, 0, },
		{ 0, 0, 0, 0, },
		{ 1, 0, 3, 0, },
		{ 0, 0, 0, 0, },
	};
	SparseMatrix<int> sm((int*)array, 5, 4, 0);
	sm.Display();

	SparseMatrix<int> sm1;
	sm1 = sm.FastTransport();
	cout << "转置后的矩阵为: " << endl << endl;
	sm1.Display();
}

wKioL1cUhT3hX2QwAAALrbRRIoc772.png