數據結構與算法-稀疏矩陣(三元表的實現)

        稀疏矩陣,矩陣中非零元素的個數遠小於矩陣元素總數,且非零元素的分佈沒有規律,可以稱該矩陣爲稀疏矩陣。如果還用一個row*col的矩陣去表示這些信息,浪費空間,可以對矩陣換一種存儲結構,只需要記錄矩陣中非零元素的位置和值。也就是通過三元表來完成一個稀疏矩陣的表示,此外還有通過鏈表,這裏講一下三元表的稀疏矩陣以及稀疏矩陣的打印,轉置。               三元表的結構就是下圖,注意它是按照先行後列升序的順序存放的,之所以按照這個順序存放,涉及打印的時候只需要一遍遍歷稀疏矩陣判斷就可以達到打印的目的,如果三元表中的每一項是亂序的還要達到打印的效果,每打印稀疏矩陣一個元素就要判斷這個元素是否存在三元表,大大增加了時間複雜度。


 
typedef struct  TRIPLE{
	int row;
	int col;
	int value;
}TRIPLE;

typedef struct TSMATRIX {
	TRIPLE *triple;	
	int matrix_row;
	int matrix_col;
	int num_count;    //這個相當於triple數組的長度
}TSMATRIX;

 

1、三元表的初始化

 

        初始化工作內容有:確定稀疏矩陣中元素個數以及確定稀疏矩陣的行列大小,然後給三元表申請好空間等待數據的錄入。

void initSparseMatrix(TSMATRIX **tsMatrix, int matrix_row, int matrix_col, int num_count) {
	TSMATRIX *tp;

	if(NULL != (*tsMatrix) || (matrix_row*matrix_col < num_count)
		|| matrix_col <= 0 || matrix_row <= 0) {

		printf("ERROR INPUT DATA\n");
		return;
	}

	tp = (TSMATRIX *)malloc(sizeof(TSMATRIX));

	if(tp != NULL) {  //有沒有分配到空間怎麼說
		tp->triple = (TRIPLE *)calloc(sizeof(TRIPLE), num_count);
		tp->matrix_row = matrix_row;
		tp->matrix_col = matrix_col;
		tp->num_count = num_count;
		*tsMatrix = tp;
	}
}

2、數據的錄入

    按照先行後列升序的順序錄入數據到三元表中, (row, col, value)爲一組數據;

void insertDataToMatrix(TSMATRIX *tsMatrix)  {
	int i;
	int row;
	int col;
	int value;

	printf("Insert data by row and then col ascending(row, col, value)\n");
	for(i = 0; i < tsMatrix->num_count; i++) {
		printf("[%d/%d] data: ", i+1, tsMatrix->num_count);
		scanf("%d%d%d", &row, &col, &value);
		tsMatrix->triple[i].row = row;
		tsMatrix->triple[i].col = col;
		tsMatrix->triple[i].value = value;
	}
}

3、打印稀疏矩陣

        稀疏矩陣的信息是從三元表中得到的,三元表從第一個開始判斷,然後逐個判斷稀疏矩陣的位置在三元表中是否存在,存在的話就打印三元表的元素,同時三元表到下一個三元,不存在的話就打印0元素。

void showSparseMatrix(const TSMATRIX *tsMatrix) {
	int i;
	int j;
	int index = 0;

	for(i = 0; i < tsMatrix->matrix_row; i++) {
		for(j = 0; j < tsMatrix->matrix_col; j++) {
			if(tsMatrix->triple[index].row == i &&
			 tsMatrix->triple[index].col == j) {
				printf("%3d", tsMatrix->triple[index++].value);
			}else {
				printf("%3d", 0);
			}
		}
		puts("");
	}
	puts("");
}

4、轉置

    稀疏矩陣的轉置實質上是改變三元表的存儲信息,行數據和列數據的交換,但不能是單純的交換,交換之後的三元變也應該保持先行後列升序的規則,這一點是稀疏矩陣轉置的難點,如何保持轉置後的三元表仍保持先行後列升序的順序。

    方法1:每次從原三元表中按照列下標遞增的方式逐個放到轉置三元表裏面去,轉置後的三元表列是原三元表的行,轉置後的三元錶行是原三元表的列,那就從列着手,從小到大把列放進去,行座標也是升序的,所以放到轉置後的三元錶行列也是升序的。這種方法可以達到轉置的目的,但是時間複雜度高,兩層循環是三元表的長度乘以矩陣的列,而不是一次遍歷三元表。

//按照列下標遞增的順序去找tsMatrix->triple[j].col == i
//在三元表裏面當前元素的列等於i就找到一個
TSMATRIX *transposeSparseMatrix_1(const TSMATRIX *tsMatrix) {
	int i;
	int j;
	int index = 0;
	TSMATRIX *transposeMatrix = NULL;

	initSparseMatrix(&transposeMatrix, tsMatrix->matrix_col, tsMatrix->matrix_row, tsMatrix->num_count);
	for(i = 0; i < tsMatrix->matrix_col; i++) {
		for(j = 0; j < tsMatrix->num_count; j++) {
			if(tsMatrix->triple[j].col == i) {
				transposeMatrix->triple[index].row = tsMatrix->triple[j].col;
				transposeMatrix->triple[index].col = tsMatrix->triple[j].row;
				transposeMatrix->triple[index++].value = tsMatrix->triple[j].value;
			}	
		}
		if(index >= tsMatrix->num_count) {   //如果發現此時三元表已經填滿就可以不用再進行外層循環了
			break;    
		}
	}

	return transposeMatrix;
}

    一次定位快速轉置:首先統計原矩陣中每列有多少個非零元素,也就是轉置完B的每行有多少個元素;然後計算原矩陣中每一列的元素在三元表中第幾個出現,同樣就是轉置矩陣B每一行的元素在三元表中第幾個出現;通過計算的出現位置的矩陣,挨個遍歷原三元表,直接放到轉置矩陣的準確位置,一遍遍歷三元表就可完成。

//一次快速定位轉置 總體分三步
//1、先對原三元表中的元素統計 每列有多少個非0元素,也就是轉置完B的每行有多少個非0元素
//2、通過第一步計算B矩陣每一行元素在三元表中第幾個開始
//3、把三元表A的元素直接放到B的三元表裏面去 一次遍歷三元表A防止B中去
TSMATRIX *transposeSparseMatrix_3(const TSMATRIX *tsMatrix) {
	int *nums = NULL;
	int *postion = NULL;
	int i;
	TSMATRIX *transposeMatrix = NULL;

//對轉轉置後的三元表初始化申請空間	
	initSparseMatrix(&transposeMatrix, tsMatrix->matrix_col, tsMatrix->matrix_row, tsMatrix->num_count);
//申請一個記錄元素出現位置的矩陣 長度和原稀疏矩陣列保持一致	
	postion = (int *)calloc(sizeof(int), tsMatrix->matrix_col);
	nums = (int *)calloc(sizeof(int), tsMatrix->matrix_col);
	for(i = 0; i < tsMatrix->num_count; i++) {
//遍歷原三元表,統計矩陣的每一列有多少個非零元素 
//對於矩陣
//0 14 0 0  -5
//0 -7 0 0   0
//36 0 0 28  0
//每一列包含的元素個數爲 1 2 0 1 1		
		nums[tsMatrix->triple[i].col]++;
	}
	for(i = 1, postion[0]= 1; i < tsMatrix->matrix_col; i++) {
//通過上一步計算的矩陣中每一列有多少個非零元素 計算矩陣的每一列元素在三元表出現的位置
//即是1 2 4 4 5	
		postion[i] = nums[i - 1] + postion[i - 1];
	}
	

	for(i = 0; i < tsMatrix->num_count; i++) {
//原三元表當前這一項在轉置的三元表中應該出現的位置		
		int index = postion[tsMatrix->triple[i].col];
		transposeMatrix->triple[index - 1].row = tsMatrix->triple[i].col;
		transposeMatrix->triple[index - 1].col = tsMatrix->triple[i].row;
		transposeMatrix->triple[index - 1].value = tsMatrix->triple[i].value;
//該列的一個元素已經有一個放進去 位置往後移一個		
		postion[tsMatrix->triple[i].col]++;    //當前列的開始位置加1
	}

	free(postion);
	free(nums);

	return transposeMatrix;
}

 

 

轉置的測試

完整的代碼:

SparseMatrix.c

#include<stdio.h>
#include<malloc.h>

#include"SparseMatrix.h"

//一次快速定位轉置 總體分三步
//1、先對原三元表中的元素統計 每列有多少個非0元素,也就是轉置完B的每行有多少個非0元素
//2、通過第一步計算B矩陣每一行元素在三元表中第幾個開始
//3、把三元表A的元素直接放到B的三元表裏面去 一次遍歷三元表A防止B中去
TSMATRIX *transposeSparseMatrix_3(const TSMATRIX *tsMatrix) {
	int *nums = NULL;
	int *postion = NULL;
	int i;
	TSMATRIX *transposeMatrix = NULL;

//對轉轉置後的三元表初始化申請空間	
	initSparseMatrix(&transposeMatrix, tsMatrix->matrix_col, tsMatrix->matrix_row, tsMatrix->num_count);
//申請一個記錄元素出現位置的矩陣 長度和原稀疏矩陣列保持一致	
	postion = (int *)calloc(sizeof(int), tsMatrix->matrix_col);
	nums = (int *)calloc(sizeof(int), tsMatrix->matrix_col);
	for(i = 0; i < tsMatrix->num_count; i++) {
//遍歷原三元表,統計矩陣的每一列有多少個非零元素 
//對於矩陣
//0 14 0 0  -5
//0 -7 0 0   0
//36 0 0 28  0
//每一列包含的元素個數爲 1 2 0 1 1		
		nums[tsMatrix->triple[i].col]++;
	}
	for(i = 1, postion[0]= 1; i < tsMatrix->matrix_col; i++) {
//通過上一步計算的矩陣中每一列有多少個非零元素 計算矩陣的每一列元素在三元表出現的位置
//即是1 2 4 4 5	
		postion[i] = nums[i - 1] + postion[i - 1];
	}
	

	for(i = 0; i < tsMatrix->num_count; i++) {
//原三元表當前這一項在轉置的三元表中應該出現的位置		
		int index = postion[tsMatrix->triple[i].col];
		transposeMatrix->triple[index - 1].row = tsMatrix->triple[i].col;
		transposeMatrix->triple[index - 1].col = tsMatrix->triple[i].row;
		transposeMatrix->triple[index - 1].value = tsMatrix->triple[i].value;
//該列的一個元素已經有一個放進去 位置往後移一個		
		postion[tsMatrix->triple[i].col]++;    //當前列的開始位置加1
	}

	free(postion);
	free(nums);

	return transposeMatrix;
}

//按照列下標遞增的順序去找tsMatrix->triple[j].col == i
//在三元表裏面當前元素的列等於i就找到一個
TSMATRIX *transposeSparseMatrix_1(const TSMATRIX *tsMatrix) {
	int i;
	int j;
	int index = 0;
	TSMATRIX *transposeMatrix = NULL;

	initSparseMatrix(&transposeMatrix, tsMatrix->matrix_col, tsMatrix->matrix_row, tsMatrix->num_count);
	for(i = 0; i < tsMatrix->matrix_col; i++) {
		for(j = 0; j < tsMatrix->num_count; j++) {
			if(tsMatrix->triple[j].col == i) {
				transposeMatrix->triple[index].row = tsMatrix->triple[j].col;
				transposeMatrix->triple[index].col = tsMatrix->triple[j].row;
				transposeMatrix->triple[index++].value = tsMatrix->triple[j].value;
			}	
		}
		if(index >= tsMatrix->num_count) {   //如果發現此時三元表已經填滿就可以不用再進行外層循環了
			break;    
		}
	}

	return transposeMatrix;
}

void showThreeTable(const TSMATRIX *tsMatrix) {
	int i;

	for(i = 0; i <tsMatrix->num_count; i++) {
		printf("(%d,%d,%d)\n", 
			tsMatrix->triple[i].row,
			tsMatrix->triple[i].col, 
			tsMatrix->triple[i].value);
	}
}

void showSparseMatrix(const TSMATRIX *tsMatrix) {
	int i;
	int j;
	int index = 0;

	for(i = 0; i < tsMatrix->matrix_row; i++) {
		for(j = 0; j < tsMatrix->matrix_col; j++) {
			if(tsMatrix->triple[index].row == i &&
			 tsMatrix->triple[index].col == j) {
				printf("%3d", tsMatrix->triple[index++].value);
			}else {
				printf("%3d", 0);
			}
		}
		puts("");
	}
	puts("");
}
void insertDataToMatrix(TSMATRIX *tsMatrix)  {
	int i;
	int row;
	int col;
	int value;

	printf("Insert data by row and then col ascending(row, col, value)\n");
	for(i = 0; i < tsMatrix->num_count; i++) {
		printf("[%d/%d] data: ", i+1, tsMatrix->num_count);
		scanf("%d%d%d", &row, &col, &value);
		tsMatrix->triple[i].row = row;
		tsMatrix->triple[i].col = col;
		tsMatrix->triple[i].value = value;
	}
}

void destorySparseMatrix(TSMATRIX **tsMatrix) {
	if(*tsMatrix == NULL) {
		return;
	}

	free((*tsMatrix)->triple);
	free(*tsMatrix);
	*tsMatrix = NULL;
}

void initSparseMatrix(TSMATRIX **tsMatrix, int matrix_row, int matrix_col, int num_count) {
	TSMATRIX *tp;

	if(NULL != (*tsMatrix) || (matrix_row*matrix_col < num_count)
		|| matrix_col <= 0 || matrix_row <= 0) {

		printf("ERROR INPUT DATA\n");
		return;
	}

	tp = (TSMATRIX *)malloc(sizeof(TSMATRIX));

	if(tp != NULL) {  //有沒有分配到空間怎麼說
		tp->triple = (TRIPLE *)calloc(sizeof(TRIPLE), num_count);
		tp->matrix_row = matrix_row;
		tp->matrix_col = matrix_col;
		tp->num_count = num_count;
		*tsMatrix = tp;
	}
}

SparseMatrix.h

Demo.c

#ifndef _SPARSE_MATRIX_H
#define _SPARSE_MATRIX_H

typedef struct  TRIPLE{
	int row;
	int col;
	int value;
}TRIPLE;

typedef struct TSMATRIX {
	TRIPLE *triple;	
	int matrix_row;
	int matrix_col;
	int num_count;    //這個相當於triple數組的長度
}TSMATRIX;

void initSparseMatrix(TSMATRIX **tsMatrix, int matrix_row, int matrix_col, int num_count);
void destorySparseMatrix(TSMATRIX **tsMatrix);
void showSparseMatrix(const TSMATRIX *tsMatrix);
void insertDataToMatrix(TSMATRIX *tsMatrix);
//對一個矩陣進行轉置
TSMATRIX *transposeSparseMatrix_1(const TSMATRIX *tsMatrix);

//一次定位轉置 較前兩種轉置降低時間複雜度對一個矩陣進行轉置
TSMATRIX *transposeSparseMatrix_3(const TSMATRIX *tsMatrix);

//打印矩陣的三元表
void showThreeTable(const TSMATRIX *tsMatrix);

#endif

#include<stdio.h>

#include"SparseMatrix.h"

int main(void) {
	TSMATRIX *tsMatrix = NULL;
	TSMATRIX *transposeMatrix = NULL;
	int matrix_col;
	int matrix_row;
	int num_count;

	printf("input row col and num_count: ");
	scanf("%d%d%d", &matrix_row, &matrix_col, &num_count);

	initSparseMatrix(&tsMatrix, matrix_row, matrix_col, num_count);
	insertDataToMatrix(tsMatrix);
	showSparseMatrix(tsMatrix);
	transposeMatrix = transposeSparseMatrix_3(tsMatrix);
	showSparseMatrix(transposeMatrix);

	destorySparseMatrix(&tsMatrix);
	destorySparseMatrix(&transposeMatrix);

	return 0;
}
/*
4 5 9
0 0 1
0 2 3
0 3 4
1 1 2
1 3 1
2 2 3
2 4 1
3 1 1
3 3 1

3 5 5
0 1 14
0 4 -5
1 1 -7
2 0 36
2 3 28
*/

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章