特殊矩陣的壓縮存儲

  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]。

650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="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的矩陣,矩陣中有效值的個數遠小於無效值的個數,且這些數據的分佈沒有規律

 比如下面這個矩陣:

 650) this.width=650;" src="http://s2.51cto.com/wyfs02/M01/7F/1D/wKioL1cUekXTHIOVAAAJWXulRyo033.png" title="QQ截圖20160418140641.png" alt="wKioL1cUekXTHIOVAAAJWXulRyo033.png" />

650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" /> (1)稀疏矩陣的壓縮存儲

   壓縮存儲只存儲極少數的有效數據。使用{row,col,value}三元組存儲每一個有效數據,三元組按原矩陣中的位置,以行優先級先後順序依次存放。

  那麼上面的矩陣壓縮存儲結果就是:

  650) this.width=650;" src="http://s5.51cto.com/wyfs02/M00/7F/20/wKiom1cUefrgD4A3AAAEUJora9M325.png" title="QQ截圖20160418140843.png" alt="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]位置上的數據對換。

 650) this.width=650;" src="http://s2.51cto.com/wyfs02/M00/7F/1D/wKioL1cUfJXBya7rAAAW17qk6v4073.png" title="QQ截圖20160418141640.png" alt="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個元素應該存放的位置。

       650) this.width=650;" src="http://s2.51cto.com/wyfs02/M00/7F/1E/wKioL1cUgh7xfJ6XAAAl71YfXUA333.png" title="QQ截圖20160418144014.png" alt="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();
}

650) this.width=650;" src="http://s4.51cto.com/wyfs02/M00/7F/1E/wKioL1cUhT3hX2QwAAALrbRRIoc772.png" title="QQ截圖20160418145336.png" alt="wKioL1cUhT3hX2QwAAALrbRRIoc772.png" />

本文出自 “不斷進步的空間” 博客,請務必保留此出處http://10824050.blog.51cto.com/10814050/1765046

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章