当数组中非零元素非常少时,我们可以称之为稀疏矩阵。为了减少空间的浪费,在存储稀疏矩阵的时候,我们对它进行压缩存储,即指存储非零信息。
一、数组的顺序存储
在存储稀疏矩阵的时候,除了存储非零元素的值,还需要存储其行列号i和j,故每个非零元素要用一个三元组(i, j, v)来描述。因为要与矩阵的对应关系,我们还需要在三元组表中增设非零元素的个数和矩阵行列大小。
那么,稀疏矩阵的数组表达如下所示。
struct CrossNode{ //三元组结构
int i, j; //行列号
ElementType value; //元素值
CrossNode(int idx, int jdx, ElementType val):
i(idx), j(jdx), value(val){}
};
class CrossList{ //三元组表结构
int row, col, count; //行列数、非零元素个数
CrossNode data[maxnum];
}
二、十字链表
当三元组按行列序组成一个线性表,如果数组的非零元素个数较大,元素定位的效率就会比较低下。为此,我们可以采用行列两个方向交叉的十字链表,来表示稀疏矩阵。
每个非零节点不仅要存放(i, j, e),还要存放它横向的下一个结点的地址以及纵向的下一个结点的地址,形成一个类似十字形的链表的结构,具体定义如下:
//节点结构
struct CrossNode{
int i, j, value;
CrossNode *right, *down;
CrossNode(int idx, int jdx, int val) : i(idx), j(jdx), value(val), right(NULL), down(NULL){}
};
typedef struct CrossNode CLNode, *CrossLine;
//十字链表
class CrossList{
public:
CrossList(){}
CrossList(vector< vector<int> > &arr);
//...
private:
//...
int row, col, count;
CrossLine *rowHead, *colHead;
};
这里需要注意的是,十字链表结构里的rowHead和colHead的类型是CorssLine *,也就是CrossNode * * 。
如果把它们设置为CrossNode *类型,那么后期元素的定位和访问需要行和列的遍历,效率不高。
完整程序
#include <iostream>
#include <vector>
using namespace std;
//Node Denifition
struct CrossNode{
int i, j, value;
CrossNode *right, *down;
CrossNode(int idx, int jdx, int val) : i(idx), j(jdx), value(val), right(NULL), down(NULL){}
};
typedef struct CrossNode CLNode, *CrossLine;
//Orthogonal List Definition
class CrossList{
public:
CrossList(){}
CrossList(vector< vector<int> > &arr);
void Insert(CLNode* &node);
void print();
int getCount();
private:
void InsertIntoRow(CLNode* &node);
void InsertIntoColumn(CLNode* &node);
void CreateSparseMatrix(vector< vector<int> > &arr);
int row, col, count;
CrossLine *rowHead, *colHead;
};
//Constructor
CrossList::CrossList(vector< vector<int> > &arr){
if(arr.empty()) return;
row = arr.size();
col = arr[0].size();
rowHead = new CrossLine[row];
for(int m = 0; m < row; ++m)
rowHead[m] = NULL;
colHead = new CrossLine[col];
for(int n = 0; n < col; ++n)
colHead[n] = NULL;
CreateSparseMatrix(arr);
}
//Insertion
void CrossList::Insert(CLNode* &node){
InsertIntoRow(node);
InsertIntoColumn(node);
}
//Update rowHead when insertion
void CrossList::InsertIntoRow(CLNode* &node){
int m = node->i;
if(!rowHead[m]){
rowHead[m] = node;
}
else{
if(rowHead[m]->j > node->j){
node->right = rowHead[m];
rowHead[m] = node;
}
else{
CLNode* cur = rowHead[m];
while(cur->right && cur->right->j < node->j){
cur = cur->right;
}
if(!cur->right){
cur->right = node;
}
else{
node->right = cur->right;
cur->right = node;
}
}
}
}
//Update colHead when insertion
void CrossList::InsertIntoColumn(CLNode* &node){
int n = node->j;
if(!colHead[n]){
colHead[n] = node;
}
else{
if(colHead[n]->i < node->i){
node->down = colHead[n];
colHead[n] = node;
}
else{
CrossLine cur = colHead[n]; //CLNode *
while(cur->down && cur->down->i < node->i){
cur = cur->down;
}
if(!cur->down){
cur->down = node;
}
else{
node->down = cur->down;
cur->down = node;
}
}
}
}
//Store sparse matrix
void CrossList::CreateSparseMatrix(vector< vector<int> > &arr){
for(int m = 0; m < row; ++m){
for(int n = 0; n < col; ++n){
if(arr[m][n] != 0){
CLNode *newNode = new CLNode(m, n, arr[m][n]);
Insert(newNode);
++count;
}
}
}
}
//Print sparse matrix
void CrossList::print(){
for(int m = 0; m < row; ++m){
CrossLine cur = rowHead[m];
int n = 0;
while(n < col){
if(cur && n == cur->j){
cout << cur->value << " ";
cur = cur->right;
}
else cout << "0 ";
++n;
}
cout << endl;
}
}
int CrossList::getCount(){
return count;
}
//Test case
int main(){
vector< vector<int> > arr = {
{0, 18, 0, 0, 0, 0},
{0, 0, 67, 0, 0, 0},
{0, 0, 0, 0, 0, 41},
{0, 0, 47, 62, 0, 0},
{0, 0, 0, 0, 0, 35}
};
CrossList testList(arr);
testList.print();
}