對稱矩陣及對稱矩陣的壓縮存儲
設一個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="/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}三元組存儲每一個有效數據,三元組按原矩陣中的位置,以行優先級先後順序依次存放。
那麼上面的矩陣壓縮存儲結果就是:
三元組的定義:
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]位置上的數據對換。
普通轉置(列轉置)
按照原矩陣的列優先把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個元素應該存放的位置。
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();
}
本文出自 “不斷進步的空間” 博客,請務必保留此出處http://10824050.blog.51cto.com/10814050/1765046