背景需求
在一个矩阵中,如果非0元素远远少于0元素,那么这个矩阵就是稀疏矩阵,在实际应用中,计算机会耗费大量的空间存储这些无意义的0元素,如果能够压缩一下,将会减少计算机的开销。本篇文章以五子棋的棋局保存为背景,用c语言实现原始稀疏矩阵转换为压缩矩阵并且存储为文件,棋局复盘则读取文件将压缩矩阵转换为原始稀疏矩阵。
图解
假如有如下棋局,如果要保存,可以用1来表示红棋子,2来表示黑棋子,0表示无棋子。
需求就是将上述棋局转换为如下矩阵
算法
棋局保存:1、统计有多少个有效元素count
2、定义长度为count+1,宽度为3的矩阵arr
3、逐一赋值,存入文件
棋局复盘:1、读取文件第一行,得到原始矩阵的行列数,定义原始矩阵
2、将文件中对应的非0值赋值给原始矩阵
棋局保存代码实现
#include <stdio.h>
#include <stdlib.h>
#define LEN 6
/**
求输出稀疏矩阵
*/
void showArr(int *arr, int row,int col){
int num = row*col;
int cnt = 0;
for(int i = 0 ; i < num ; i++){
printf("%4d",*(arr+i));
cnt++;
if(cnt%3==0) printf("\n");
}
}
/**
求输出原始方阵
*/
void showoldArr(int arr[][LEN]){
for(int i = 0 ; i < LEN ; i++){
for(int j = 0 ; j < LEN ; j++){
printf("%4d",arr[i][j]);
}
if(j == LEN) printf("\n");
}
}
/**
求有效元素个数
*/
int arrNum(int arr[][LEN]){
int count = 0;
for(int i = 0 ; i < LEN ; i++){
for(int j = 0 ; j < LEN ; j++){
if(arr[i][j]!=0) count++;
}
}
return count;
}
int main(){
int i = 0,j=0,k = 0,m = 0;
int oldArr[LEN][LEN] = {{0,1},{0,0,2},{1}};
//1表示白棋子,2表示黑棋子,0表示无棋子
printf("原始矩阵:\n");
showoldArr(oldArr);
//得到有效数据的个数,确定稀疏矩阵的行数
int num = arrNum(oldArr);
int row = num+1;
int col = 3;
//创建稀疏矩阵,因为c语言中的二维数组长度不能含有变量,所以用指针的指针来定义
int *newArr;
newArr=(int *)malloc(sizeof(int)*(num+1)*3);
//先给稀疏矩阵的第一行赋值
*newArr = LEN;
*(newArr+1) = LEN;
*(newArr+2) = num;
//再给剩下的行赋值
for(i = 3 ; i < row*col ; i+=3){
for(; k < LEN ; ){
for(m = 0 ; m < LEN ; m++){
if(oldArr[k][m]!=0){
*(newArr+i) = k;
*(newArr+i+1) = m;
*(newArr+i+2) = oldArr[k][m];
}
}
k+=1;
break;
}
}
printf("稀疏矩阵:\n");
showArr(newArr,row,col);
//将矩阵写入文件
FILE *p=fopen("arr.txt","w");
for(i = 0 ; i < row*col ;){
fprintf(p,"%d",*(newArr+i));
i++;
if(i%3==0&&i!=row*col) fprintf(p,"%s","\n");
}
fclose(p);
}
棋局保存运行结果
棋局复盘代码实现
#include <stdio.h>
#include <stdlib.h>
/**
输出原始数组
*/
void showArr(int *arr, int row,int col){
int num = row*col;
int cnt = 0;
for(int i = 0 ; i < num ; i++){
printf("%4d",*(arr+i));
cnt++;
if(cnt%row==0) printf("\n");
}
}
int main(){
char line[256];
FILE *f = fopen("arr.txt", "rb");
//先读第一行
fgets(line, 256, f);
int ele = atoi(line);
char rlen =ele/100; //得到矩阵长度
char clen =ele/10%10; //得到矩阵宽度
int *arr; //定义一个空间来放原始矩阵
arr = (int *)malloc(sizeof(int)*rlen*clen);//根据矩阵长度宽度开辟空间
for(int j = 0;j<rlen*clen;j++){*(arr+j) =0;} //先将所有的值赋值为0
while (!feof(f) && !ferror(f)) { //开始按行读取数据
fgets(line,sizeof(line),f);
for(int i = 0;i<rlen*clen;i++){
int ele =atoi(line);
int row = ele/100; //得到行
int col = ele/10%10; //得到列
int val = ele%10;//得到值
if((row*rlen+col)==i) //如果此时的行列正好是目标行列,赋值即可
*(arr+i) = val;
}
}
showArr(arr,rlen,clen);
fclose(f);
return 0;
}
棋局复盘运行结果
总结
c语言中,数组的长度定义不可以为变量,必须是常量,因此这里便用指针开辟空间来代替二维数组,实际上二维数组的物理内存和一位数组一样,都是线性存储,因此实质上是一样的。指针的取值怎么取也是容易出错的地方,如果写错表达式,会出现程序停止。