【C++】矩陣的壓縮存儲,還原,轉置

矩陣的格式相當於是一個二維數組,如下圖

上圖就是一個特殊的矩陣,對稱矩陣

一、對稱矩陣

對稱矩陣中的元素有一定的規律,就是行下標row與列下標col 互換後得到的新座標的元素與換座標前的元素相同。

因此我們在存放對稱矩陣的元素時只需將它的下三角或上三角的元素存儲就可以了。


我們可以用一個類來管理這個矩陣,用容器vector來存儲它的下三角的元素。(vector相當於是一個一維數組,使用時要引用頭文件 #include<vector>

(1)構造函數,將元素存入(壓縮存儲)

SymmetricMatrix(size_t row,size_t col,T*array)
		:_row(row)
		, _col(col)
	{
		int index = 0;
		_v.resize(((1 + row)*row) >> 1);      //元素總個數爲 ((1+row)*row)/2
		for (size_t i = 0; i < row;i++)
		for (size_t j = 0; j <= i; j++)         //每一行只存儲到行下標與列下表相等的地方結束
			_v[index++] = array[i*col + j];    //每存一個數讓index向後移動
	
	}
(2)元素訪問

T& Aescc(size_t row,size_t col)
	{
		if (row < col)
			swap(row,col);
		return _v[(((row + 1)*row) >> 1)+col];
	}
當行下標row小於列下標col時,表示訪問的是上三角的元素,而對稱矩陣中上三角中的元素與行列座標相反的下三角中的元素相同,因此,將行座標與列座標互換,再取出元素就行了。




如圖,當訪問(2,1)時取到元素1

當訪問(0,3)時,取得(3,0)處元素3


(3)打印對稱矩陣

void BackSymmetricMatrix( )
	{
		for (size_t i = 0; i < _row; i++)
		{
			for (size_t j = 0; j < _col; j++)
			{
				cout << Aescc(i, j) << " ";
			}
			cout << endl;
		}
	}

打印時要用到兩層循環,再在函數內調用訪問函數Aescc()就可以了


(4)對稱矩陣完整代碼:

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

#if 1                
template<class T>                //對稱矩陣
class SymmetricMatrix
{
public:
	SymmetricMatrix(size_t row,size_t col,T*array)
		:_row(row)
		, _col(col)
	{
		int index = 0;
		_v.resize(((1 + row)*row) >> 1);
		for (size_t i = 0; i < row;i++)
		for (size_t j = 0; j <= i; j++)
			_v[index++] = array[i*col + j];
	
	}
	T& Aescc(size_t row,size_t col)
	{
		if (row < col)
			swap(row,col);
		return _v[(((row + 1)*row) >> 1)+col];
	}

	void BackSymmetricMatrix( )
	{
		for (size_t i = 0; i < _row; i++)
		{
			for (size_t j = 0; j < _col; j++)
			{
				cout << Aescc(i, j) << " ";
			}
			cout << endl;
		}
	}

private:
	vector<T> _v;
	size_t _row;
	size_t _col;
};

int main()          //測試代碼
{
	int a[5][5] = { 
	{ 0, 1, 2, 3, 4 },
	{ 1, 0, 1, 2, 3 },
	{ 2, 1, 0, 1, 2 }, 
	{ 3, 2, 1, 0, 1 },
	{ 4, 3, 2, 1, 0 } };

	SymmetricMatrix<int> sm(5,5,(int*)a);
	/*int ret = sm.Aescc(2, 1);
	cout << ret << endl;
    ret = sm.Aescc(0, 3);
	cout << ret << endl;*/
	sm.BackSymmetricMatrix();

	system("pause");
	return 0;
}


二、稀疏矩陣


該矩陣是一個6行5列的稀疏矩陣,在存儲稀疏矩陣時,我們採用壓縮存儲,只存儲有效元素,上圖中我們存入元素後爲{1,3,5,1,3,5};其它元素爲無效元素不進行存儲,這樣更節省空間。

因爲稀疏矩陣沒有規律,所以不能像對稱矩陣那樣只存儲元素,我們還要存儲它的下標,否則無法還原稀疏矩陣。

則我們要創建一個包含它的行座標row 列座標col 與元素 data 的結構體。


該結構體是一個模板類型的,構造函數給的是帶缺省的,可以不傳參。

(1)有效元素的存儲,構造函數

SparseMatrix(T*_array,size_t row, size_t col)
		:_row(row)
		, _col(col)
		, invaild(0)
	{
		for (size_t i = 0; i < row; i++)
		{
			for (size_t j = 0; j < col; j++)
			{
				if (_array[i*col + j] != invaild)       //當不是無效值時才存
					_vm.push_back(Trituple<T>(i, j, _array[i*col + j]));
			}
		}
	}
傳參時將二維數組轉換成一維數組進行訪問

存儲結果



(2)元素訪問

T&Aescc(size_t row,size_t col)
	{
		for (size_t i = 0; i < _vm.size(); i++)
		{
			if (_vm[i]._row2 == row&&_vm[i]._col2 == col)
				return _vm[i]._data;
		}
		return invaild;
	}
每傳一個座標進入訪問函數,就去遍歷存入的有效元素,如果傳入的行座標與有效元素的行座標相等,並且列座標與有效元素的列座標相等,則返回這個有效元素。當將所有的有效元素搜索完時還未找到,返回無效值。


(3)打印稀疏矩陣

void displaySparseMatrix()        //打印稀疏矩陣
	{
	size_t index = 0;
		for (size_t i = 0; i < _row; i++)
		{
			for (size_t j = 0; j < _col; j++)
			{
				if (index<_vm.size()&&_vm[index]._row2 == i&&_vm[index]._col2 == j)
				{
					cout << _vm[index]._data << " ";
					index++;
				}
				else
					cout << invaild<<" ";
			}
			cout << endl;
		}
	}

打印函數,兩層循環,第一層循環控制行數,第二層控制列數,在引入一個索引index。當index<有效元素的個數時,並且有效元素的行座標_row2==i和有效元素的列座標_col2==j,我們就打印這個元素,否則輸出無效值。當每輸出_col個元素就換行。



我們還可以重載輸出運算符,這樣更方便

friend ostream& operator<<(ostream&_cout, SparseMatrix<T>&m)
	{
		size_t index = 0;
		for (size_t i = 0; i < m._row; i++)
		{
			for (size_t j = 0; j < m._col; j++)
			{
				if (index<m._vm.size() && m._vm[index]._row2 == i&&m._vm[index]._col2 == j)
				{
					_cout << m._vm[index]._data << " ";
					index++;
				}
				else
					_cout << m.invaild << " ";
			}
			cout << endl;
		}
		return _cout;
	}
當需要打印稀疏矩陣時,直接調用  cout<< sn1<<endl;(sn1爲稀疏矩陣對象),這樣輸出更簡單。

(4)轉置(一般方法)時間複雜度比較大爲(O(_col*_row*size));size是有效元素的個數

void ReverseMatrix()              //矩陣轉置
	{   
		for (size_t i = 0; i < _col; i++)     
		{
			for (size_t j = 0; j < _row; j++)
			{
				int flag = 0;  //標記  
				size_t index = _vm.size();
				while (index--)
				{
					if (_vm[index]._row2 == j&&_vm[index]._col2==i)
					{
						flag = 1;
						cout << _vm[index]._data << " ";
					}
				}
				if (flag==0)
				cout << invaild << " ";
			}
			cout << endl;
		}
	}
兩層循環打印出一個稀疏矩陣,轉置後的行數爲原來矩陣的列數,轉置後的列數爲原來矩陣的行數。

每到一個座標,進入有效元素裏找一遍,如果該座標的行等於有效元素的列,並且該座標的列等於有效元素的行,就輸出這個有效元素。否則輸出無效值。



(5)快速轉置

SparseMatrix<T>& FastReverseMatrix()      //快速逆置
	{
		SparseMatrix<T> *Newm=new SparseMatrix<T>;
		(*Newm)._row = _col;
		(*Newm)._col = _row;
		(*Newm).invaild = invaild;
		(*Newm)._vm.resize(_vm.size());

         //逆置成功後每一行有效元素的個數
		int *count = new int[_row];
		memset(count, 0, _row*sizeof(int));
		for (size_t i = 0; i < _vm.size(); i++)
			count[_vm[i]._col2]++;
	 //逆置成功後後每行的有效元素的起始地址
		int *addr = new int[_col];
		memset(addr, 0, _col*sizeof(int));
		for (size_t i = 1; i < _col; i++)
			addr[i] = addr[i - 1] + count[i - 1];
		for (size_t i = 0; i < _vm.size(); i++)
		{
			int &rowAddr = addr[_vm[i]._col2];
			(*Newm)._vm[rowAddr] = _vm[i];
			swap((*Newm)._vm[rowAddr]._row2, (*Newm)._vm[rowAddr]._col2);
			rowAddr++;
		}
		return (*Newm);
	}




(6)稀疏矩陣加法

要實現兩個矩陣的加法,我們與要重載+。

我們可以通過偏移量來確定元素位置,

<1>當偏移量相等時,並且兩個矩陣在該位置的值都爲有效值,則兩個值相加,當這兩個值相加得到的值不爲無效值時,存入到新創建的矩陣tmp中並將左邊矩陣的有效元素的下標加1,右邊矩陣的有效元素下標加1。

<2>當左邊矩陣有效元素的偏移量大,則將右邊矩陣的有效元素存入tmp並將右邊矩陣的有效元素的下標加1.

<3>當右邊矩陣有效元素的偏移量大,則將左邊矩陣的有效元素存入tmp並將左邊矩陣的有效元素的下標加1.

<4>循環結束,則矩陣的有效元素有可能沒有加完,將剩下的有效元素存入tmp,完成加法。

<5>返回tmp。





SparseMatrix<T>& operator+( SparseMatrix<T>& d)          //重載+,實現兩個矩陣的加法
	{
		assert(d._row==_row&&d._col==_col);
		SparseMatrix<T> *tmp=new SparseMatrix<T>;
		(*tmp)._col = _col;
		(*tmp)._row = _row;
		(*tmp).invaild = invaild;

		size_t leftMatrix = 0;
		size_t rightMatrix = 0;
		size_t Addrleft = 0;
		size_t Addrright = 0;
		while (leftMatrix < _vm.size() && rightMatrix < d._vm.size())
		{
			Addrleft = _vm[leftMatrix]._row2*_col + _vm[leftMatrix]._col2;
			Addrright = d._vm[rightMatrix]._row2*_col + d._vm[rightMatrix]._col2;
			if (Addrleft>Addrright)
			{
				(*tmp)._vm.push_back(d._vm[rightMatrix]);
				rightMatrix++;
			}
			else if (Addrleft < Addrright)
			{
				(*tmp)._vm.push_back(_vm[leftMatrix]);
				leftMatrix++;
			}
			else
			{
				Trituple<T> ret;
				ret._col2 = _vm[leftMatrix]._col2;
				ret._row2 = _vm[leftMatrix]._row2;
				ret._data = _vm[leftMatrix]._data + d._vm[rightMatrix]._data;
				if (ret._data!=invaild)
				(*tmp)._vm.push_back(ret);
				leftMatrix++;
				rightMatrix++;
			}
		}
		while (leftMatrix < _vm.size())
		{
			(*tmp)._vm.push_back(_vm[leftMatrix]);
			leftMatrix++;
		}
		while (rightMatrix < d._vm.size())
		{
			(*tmp)._vm.push_back(d._vm[rightMatrix]);
			rightMatrix++;
		}
		return (*tmp);
	}
測試


相加後的值



(7)稀疏矩陣的完整代碼

//稀疏矩陣
template<class T>
class SparseMatrix
{
public:
	template<class T>
	struct Trituple        //三元結構
	{
		Trituple(size_t row = 0, size_t col = 0, const T&data = T())
		:_row2(row)
		, _col2(col)
		, _data(data)
		{}
		size_t _row2;
		size_t _col2;
		T _data;
	};
	SparseMatrix()
	{ }
	SparseMatrix(T*_array,size_t row, size_t col)
		:_row(row)
		, _col(col)
		, invaild(0)
	{
		for (size_t i = 0; i < row; i++)
		{
			for (size_t j = 0; j < col; j++)
			{
				if (_array[i*col + j] != invaild)
					_vm.push_back(Trituple<T>(i, j, _array[i*col + j]));
			}
		}
	}
	T&Aescc(size_t row,size_t col)
	{
		for (size_t i = 0; i < _vm.size(); i++)
		{
			if (_vm[i]._row2 == row&&_vm[i]._col2 == col)
				return _vm[i]._data;
		}
		return invaild;
	}
	void displaySparseMatrix()        //打印稀疏矩陣
	{
	size_t index = 0;
		for (size_t i = 0; i < _row; i++)
		{
			for (size_t j = 0; j < _col; j++)
			{
				if (index<_vm.size()&&_vm[index]._row2 == i&&_vm[index]._col2 == j)
				{
					cout << _vm[index]._data << " ";
					index++;
				}
				else
					cout << invaild<<" ";
			}
			cout << endl;
		}
	}

	void ReverseMatrix()              //矩陣轉置
	{   
		for (size_t i = 0; i < _col; i++)
		{
			for (size_t j = 0; j < _row; j++)
			{
				int flag = 0;  //標記  
				size_t index = _vm.size();
				while (index--)
				{
					if (_vm[index]._row2 == j&&_vm[index]._col2==i)
					{
						flag = 1;
						cout << _vm[index]._data << " ";
					}
				}
				if (flag==0)
				cout << invaild << " ";
			}
			cout << endl;
		}
	}
	friend ostream& operator<<(ostream&_cout, SparseMatrix<T>&m)
	{
		size_t index = 0;
		for (size_t i = 0; i < m._row; i++)
		{
			for (size_t j = 0; j < m._col; j++)
			{
				if (index<m._vm.size() && m._vm[index]._row2 == i&&m._vm[index]._col2 == j)
				{
					_cout << m._vm[index]._data << " ";
					index++;
				}
				else
					_cout << m.invaild << " ";
			}
			cout << endl;
		}
		return _cout;
	}

	SparseMatrix<T>& operator+( SparseMatrix<T>& d)          //重載+,實現兩個矩陣的加法
	{
		assert(d._row==_row&&d._col==_col);
		SparseMatrix<T> *tmp=new SparseMatrix<T>;
		(*tmp)._col = _col;
		(*tmp)._row = _row;
		(*tmp).invaild = invaild;

		size_t leftMatrix = 0;
		size_t rightMatrix = 0;
		size_t Addrleft = 0;
		size_t Addrright = 0;
		while (leftMatrix < _vm.size() && rightMatrix < d._vm.size())
		{
			Addrleft = _vm[leftMatrix]._row2*_col + _vm[leftMatrix]._col2;
			Addrright = d._vm[rightMatrix]._row2*_col + d._vm[rightMatrix]._col2;
			if (Addrleft>Addrright)
			{
				(*tmp)._vm.push_back(d._vm[rightMatrix]);
				rightMatrix++;
			}
			else if (Addrleft < Addrright)
			{
				(*tmp)._vm.push_back(_vm[leftMatrix]);
				leftMatrix++;
			}
			else
			{
				Trituple<T> ret;
				ret._col2 = _vm[leftMatrix]._col2;
				ret._row2 = _vm[leftMatrix]._row2;
				ret._data = _vm[leftMatrix]._data + d._vm[rightMatrix]._data;
				if (ret._data!=invaild)
				(*tmp)._vm.push_back(ret);
				leftMatrix++;
				rightMatrix++;
			}
		}
		while (leftMatrix < _vm.size())
		{
			(*tmp)._vm.push_back(_vm[leftMatrix]);
			leftMatrix++;
		}
		while (rightMatrix < d._vm.size())
		{
			(*tmp)._vm.push_back(d._vm[rightMatrix]);
			rightMatrix++;
		}
		return (*tmp);
	}


	SparseMatrix<T>& FastReverseMatrix()      //快速逆置
	{
		SparseMatrix<T> *Newm=new SparseMatrix<T>;
		(*Newm)._row = _col;
		(*Newm)._col = _row;
		(*Newm).invaild = invaild;
		(*Newm)._vm.resize(_vm.size());

		//逆置成功後每一行有效元素的個數
		int *count = new int[_col];
		memset(count, 0, _col*sizeof(int));
		for (size_t i = 0; i < _vm.size(); i++)
			count[_vm[i]._col2]++;
	//逆置成功後後每行的有效元素的起始地址
		int *addr = new int[_col];
		memset(addr, 0, _col*sizeof(int));
		for (size_t i = 1; i < _col; i++)
			addr[i] = addr[i - 1] + count[i - 1];
		for (size_t i = 0; i < _vm.size(); i++)
		{
			int &rowAddr = addr[_vm[i]._col2];
			(*Newm)._vm[rowAddr] = _vm[i];
			swap((*Newm)._vm[rowAddr]._row2, (*Newm)._vm[rowAddr]._col2);
			rowAddr++;
		}
		return (*Newm);
	}

	
private:
	vector <Trituple<T>> _vm;
	size_t _row;
	size_t _col;
	T invaild;
};
void test1()   //測試矩陣加法
{
	int array1[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 } };
	SparseMatrix<int> sn1((int*)array1, 6, 5);   //存儲一個稀疏矩陣
	sn1.displaySparseMatrix();     //打印稀疏矩陣
	cout << endl;

	int array2[6][5] = {
		{ 0, 0, 3, 0, 5 },
		{ 0, 2, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 },
		{ 5, 0, -3, 0, 5 },
		{ 0, 4, 0, 0, 0 },
		{ 2, 0, 0, 0, 0 } };
	SparseMatrix<int> sn2((int*)array2, 6, 5);   //存儲一個稀疏矩陣
	cout << sn2 << endl;
	SparseMatrix<int> sn3 = sn1 + sn2;
	cout << sn3;

}

void test2()   //測試矩陣快速轉置函數
{
	int array1[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 } };
	SparseMatrix<int> sn1((int*)array1, 6, 5);   //存儲一個稀疏矩陣
	SparseMatrix<int> sn2=sn1.FastReverseMatrix();
	cout << sn2;
}
int main()
{
	
	test1();
	//test2();

	system("pause");
	return 0;
}




發佈了70 篇原創文章 · 獲贊 92 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章