【数据结构】稀疏矩阵的压缩存储(三元组表、十字链表)(C语言)


稀疏矩阵是指矩阵中大多数元素为零的矩阵。从直观上讲,当非零元素个数低于总元素的30%时,这样的矩阵为稀疏矩阵。

1. 三元组表

1.1 三元组表的存储结构

稀疏矩阵的三元组表表示法是指只存储非零元素,同时存储该非零元素在矩阵中所处的行号和列号的位置信息。
三元组的结构为方便处理,将稀疏矩阵中非零元素对应的三元组按“行序为主序”的一维结构体数组进行存放,将矩阵的每一行(行由小到大)的全部非零元素的三元组按列号递增存放,得到矩阵的三元组表
矩阵与三元组表
代码

# define MAXSIZE 1000				//设非零元素的个数最多为1000
/*三元组的存储结构*/
typedef struct {
	int row, col;					//该非零元素的行下标和列下标
	int e;							//该非零元素的值
}Triple;
/*三元组表的存储结构*/
typedef struct {
	Triple data[MAXSIZE + 1];		//非零元素的三元组表,下标从1开始
	int m, n, len;					//矩阵的行数、列数和非零元素的个数
}TSMatrix;

1.2 基于三元组表的矩阵转置

① 三元组表中的三元组行列互换。
② 三元组表重新按照行下标递增排序。

1.2.1 列序递增转置法
采用按照被转置矩阵三元组表A的列序递增的顺序进行转置,并依次送入转置后矩阵的三元组表B中。
列序递增转置法

/*转置:列序递增转置法*/
void TransposeTSMatrix(TSMatrix* A, TSMatrix* B) {
	int i, j, k;
	B->m = A->n;
	B->n = A->m;
	B->len = A->len;
	if (B->len > 0) {
		j = 1;											//记录转置后的三元组在三元组表B中的下标值
		for (k = 1; k <= A->n; k++) {
			for (i = 1; i < A->len; i++) {				//从头至尾扫描三元组表A,寻找col值为k的三元组进行转置
				if (A->data[i].col == k) {
					B->data[j].row = A->data[i].col;
					B->data[j].col = A->data[i].row;
					B->data[j].e = A->data[i].e;
					j++;
				}
			}
		}
	}
}

1.2.2 一次快速转置法
只对三元组表A扫描一次,就使A中所有非零元的三元组“一次定位”直接放到三元组B中的正确位置上。

num[col]:用来存放三元组表A第col列中非零元素总个数
position[col]:用来存放转置前三元组表A中第col列中第一个非零元素在三元组表B中的存储位置(下标值)
position[1] = 1
position[col] = position[col - 1] + num[col - 1](col > 1)
一次快速转置法

/*一次定位快速转置法*/
void FastTransposeTSMatrix(TSMatrix* A, TSMatrix* B) {
	int col, t, p, q;
	int num[MAXSIZE], position[MAXSIZE];
	B->m = A->n;
	B->n = A->m;
	B->len = A->len;
	if (B->len) {
		for (col = 1; col <= A->n; col++)				//初始化num数组
			num[col] = 0;
		for (t = 1; t <= A->len; t++)
			num[A->data[t].col]++;						//扫描三元组表A,求num数组
		position[1] = 1;
		for (col = 2; col < A->n; col++)				//根据num数组求position数组
			position[col] = position[col - 1] + num[col - 1];

		for (p = 1; p <= A->len; p++) {					//扫描三元组表A,实现转置
			col = A->data[p].col;
			q = position[col];
			B->data[q].row = A->data[p].col;
			B->data[q].col = A->data[p].row;
			B->data[q].e = A->data[p].e;
			position[col]++;							//表示该列的下一个非零元素
		}
	}
}

1.3 完整实现代码

# include<stdio.h>
# define MAXSIZE 1000				//设非零元素的个数最多为1000

/*三元组*/
/*三元组的存储结构*/
typedef struct {
	int row, col;					//该非零元素的行下标和列下标
	int e;							//该非零元素的值
}Triple;
/*三元组表的存储结构*/
typedef struct {
	Triple data[MAXSIZE + 1];		//非零元素的三元组表
	int m, n, len;					//矩阵的行数、列数和非零元素的个数
}TSMatrix;

/*根据数组创建三元组表*/
void CreateTSMatrix(TSMatrix* A, int arry[][7], int m, int n) {
	int i, j, k = 1;
	for (i = 0; i < m; i++) {
		for (j = 0; j < n; j++) {
			if (arry[i][j] != 0) {
				A->data[k].row = i + 1;
				A->data[k].col = j + 1;
				A->data[k].e = arry[i][j];
				k++;
			}
		}
	}
	A->len = k - 1;
	A->m = m;
	A->n = n;
}

/*转置:列序递增转置法*/
void TransposeTSMatrix(TSMatrix* A, TSMatrix* B) {
	int i, j, k;
	B->m = A->n;
	B->n = A->m;
	B->len = A->len;
	if (B->len > 0) {
		j = 1;											//记录转置后的三元组在三元组表B中的下标值
		for (k = 1; k <= A->n; k++) {
			for (i = 1; i < A->len; i++) {				//从头至尾扫描三元组表A,寻找col值为k的三元组进行转置
				if (A->data[i].col == k) {
					B->data[j].row = A->data[i].col;
					B->data[j].col = A->data[i].row;
					B->data[j].e = A->data[i].e;
					j++;
				}
			}
		}
	}
}

/*一次定位快速转置法*/
/* num[col]用来存放三元组表A第col列中非零元素总个数
   position[col]用来存放转置前三元组表A中第col列中第一个非零元素在三元组表B中的存储位置(下标值)
   position[col]=position[col-1]+num[col-1](col>1) */
void FastTransposeTSMatrix(TSMatrix* A, TSMatrix* B) {
	int col, t, p, q;
	int num[MAXSIZE], position[MAXSIZE];
	B->m = A->n;
	B->n = A->m;
	B->len = A->len;
	if (B->len) {
		for (col = 1; col <= A->n; col++)				//初始化num数组
			num[col] = 0;
		for (t = 1; t <= A->len; t++)
			num[A->data[t].col]++;						//扫描三元组表A,求num数组
		position[1] = 1;
		for (col = 2; col < A->n; col++)				//根据num数组求position数组
			position[col] = position[col - 1] + num[col - 1];

		for (p = 1; p <= A->len; p++) {					//扫描三元组表A,实现转置
			col = A->data[p].col;
			q = position[col];
			B->data[q].row = A->data[p].col;
			B->data[q].col = A->data[p].row;
			B->data[q].e = A->data[p].e;
			position[col]++;							//表示该列的下一个非零元素
		}
	}
}

/*根据三元组表输出稀疏矩阵*/
void Display(TSMatrix* A) {
	int i, j, k = 1;
	for (i = 1; i <= A->m; i++) {
		for (j = 1; j <= A->n; j++) {
			if (i == A->data[k].row && j == A->data[k].col) {
				printf("%3d", A->data[k].e);
				k++;
			}
			else
				printf("  0");
		}
		printf("\n");
	}
}

int main() {
	int M[6][7] = { {0,12,9,0,0,0,0},
				{0,0,0,0,0,0,0},
				{-3,0,0,0,0,14,0},
				{0,0,24,0,0,0,0},
				{0,18,0,0,0,0,0},
				{15,0,0,-7,0,0,0} };
	TSMatrix A, B, C;
	CreateTSMatrix(&A, M, 6, 7);
	TransposeTSMatrix(&A, &B);
	printf("列序递增转置法:\n");
	Display(&B);
	FastTransposeTSMatrix(&A, &C);
	printf("\n一次定位快速转置法:\n");
	Display(&C);
	return 0;
}

1.4 运行结果

运行结果

2. 十字链表

2.1 十字链表的存储结构

十字链表是稀疏矩阵的链式存储法。矩阵的每一个非零元素用一个结点表示,该结点除了(row,col,vaule)以外,还要添加两个链域:right 用于链接同一行中的下一个非零元素,down 用于链接同一列中的下一个非零元素。
十字链表的结点结构十字链表代码实现

/*十字链表的存储结构*/
typedef struct OLNode {
	int row, col;						//非零元素的行和列下标
	int vaule;
	struct OLNode* right, * down;		//非零元素所在行表、列表的后继链域
}OLNode, *OLink;

typedef struct {
	OLink* row_head, * col_head;		//行、列链表的头指针向量
	int m, n, len;						//稀疏矩阵的行数、列数、非零元素个数
}CrossList;

参考:耿国华《数据结构——用C语言描述(第二版)》

更多数据结构内容关注我的《数据结构》专栏https://blog.csdn.net/weixin_51450101/category_11514538.html?spm=1001.2014.3001.5482

  • 26
    点赞
  • 136
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
稀疏矩阵是指矩阵中大部分元素为0的矩阵,而非0元素的数量相对较少。由于这种矩阵的特殊性质,我们可以采用压缩存储的方式来节省存储空间。常用的压缩存储方式有三种:行逐行压缩、列逐列压缩和十字链表压缩。下面以行逐行压缩为例,介绍C语言稀疏矩阵压缩存储及其应用。 行逐行压缩是指将稀疏矩阵的每一行转化为一个三元组(i, j, A[i][j]),其中i和j分别示非零元素的行列下标,A[i][j]示该元素的值。这样,我们就可以用一个一维数组来存储整个稀疏矩阵。具体的实现代码如下: ``` #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct { int row; int col; int val; } Triple; void create_sparse_matrix(int rows, int cols, int *matrix, int size, Triple *sparse_matrix) { int i, j, k = 0; for (i = 0; i < rows; ++i) { for (j = 0; j < cols; ++j) { if (matrix[i * cols + j] != 0) { sparse_matrix[k].row = i; sparse_matrix[k].col = j; sparse_matrix[k].val = matrix[i * cols + j]; ++k; } } } sparse_matrix[size].row = rows; sparse_matrix[size].col = cols; sparse_matrix[size].val = k; } void print_sparse_matrix(Triple *sparse_matrix, int size) { int i; printf("行\t列\t值\n"); for (i = 0; i <= size; ++i) { printf("%d\t%d\t%d\n", sparse_matrix[i].row, sparse_matrix[i].col, sparse_matrix[i].val); } } int *sparse_matrix_multiplication(Triple *a, int a_size, Triple *b, int b_size) { if (a[0].col != b[0].row) { return NULL; } int i, j, k; int *c = (int*)malloc(a[0].row * b[0].col * sizeof(int)); for (i = 0; i < a[0].row; ++i) { for (j = 0; j < b[0].col; ++j) { c[i * b[0].col + j] = 0; for (k = 0; k < a_size; ++k) { if (a[k].row == i && b[k].col == j) { c[i * b[0].col + j] += a[k].val * b[k].val; } } } } return c; } int main() { int rows, cols, i, j; int matrix[MAX_SIZE][MAX_SIZE], size; Triple *sparse_matrix; printf("请输入矩阵的行数和列数:"); scanf("%d%d", &rows, &cols); printf("请输入矩阵的所有元素:\n"); for (i = 0; i < rows; ++i) { for (j = 0; j < cols; ++j) { scanf("%d", &matrix[i][j]); } } size = 0; for (i = 0; i < rows; ++i) { for (j = 0; j < cols; ++j) { if (matrix[i][j] != 0) { ++size; } } } sparse_matrix = (Triple*)malloc((size + 1) * sizeof(Triple)); create_sparse_matrix(rows, cols, (int*)matrix, size, sparse_matrix); print_sparse_matrix(sparse_matrix, size); free(sparse_matrix); return 0; } ``` 在这个代码中,我们首先定义了一个三元组`Triple`来稀疏矩阵的一个非零元素,其中row和col分别示行列下标,val示元素值。然后定义了三个函数,`create_sparse_matrix`用于将原始矩阵转化为稀疏矩阵,`print_sparse_matrix`用于打印稀疏矩阵,`sparse_matrix_multiplication`用于计算两个稀疏矩阵的乘积。 在`create_sparse_matrix`函数中,我们首先遍历整个原始矩阵,找到所有非零元素,并将其转化为一个三元组存储稀疏矩阵中。最后,我们在稀疏矩阵的最后一行,存储原始矩阵的行列数和稀疏矩阵中非零元素的个数。在`print_sparse_matrix`函数中,我们直接遍历稀疏矩阵,打印每个三元组的行列下标和元素值。在`sparse_matrix_multiplication`函数中,我们首先判断两个矩阵是否可以相乘,然后遍历第一个矩阵的所有行和第二个矩阵的所有列,对于每个元素,找到它们在两个稀疏矩阵中的对应位置,并计算它们的乘积,最后存储在结果矩阵中。 稀疏矩阵压缩存储可以大大节省存储空间,特别是当矩阵中非零元素的数量很少时,它的优势更加明显。稀疏矩阵还可以应用于很多实际场景,比如图像处理中的图像压缩、网络流量分析中的路由优化等。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值