数据结构 - 十字链表之稀疏矩阵的存储

当数组中非零元素非常少时,我们可以称之为稀疏矩阵。为了减少空间的浪费,在存储稀疏矩阵的时候,我们对它进行压缩存储,即指存储非零信息。

一、数组的顺序存储

在存储稀疏矩阵的时候,除了存储非零元素的值,还需要存储其行列号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();
}
  • 27
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值