您当前的位置: 首页 >  数据结构与算法

刘一哥GIS

暂无认证

  • 3浏览

    0关注

    934博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

热榜!!!数据结构与算法:C语言版---数组与稀疏矩阵---强势来袭!

刘一哥GIS 发布时间:2021-07-06 14:52:49 ,浏览量:3

数组是各种计算机语言中经常使用到的重要数据结构,一般的说:在内存中申请一片连续地址的存储空间、存储这些数、就称为数组。

在C语言中,申请连续的存储空间是很容易的事情,但难在多维数组的组织、以及数组数据的压缩上,以下的介绍就是给大家说明如何组织多维空间的数组。

1 C语言的可变参数函数

在C语言中,大多教材所介绍的内容中,一个函数的参数个数是确定的,比如:

#include
double BoxVolume(double a,double b,double c)
{
return a*b*c;
}
main()
{
double x,y,z;
X=1;y=2;z=3;
prinrf(“%f\n”,BoxVolume(x,y,z));
}

函数BoxVolume()有三个参数,在实际编程中,调用这个函数不得少于三个参数、也不得多于三个参数。在main()中的调用中就是这样。

但可变参数的函数是我们在C语言中也见过的,比如:

printf(“%d %d\n”,a,b);       //有三个参数。
printf(“%d %d  %d\n”,a,b,c); //有四个参数。

同样,scanf()也是个可变参数函数,调用该函数、参数个数是不确定的。这说明C语言函数个数可以是不确定的。说明一个可变参数函数,把可变参数定义为:

就是三个小数点。如:

Fun(char ch,int m,…)
{
函数体
}

其中char ch,int m是固定参数部分,而…则代表可变参数部分。

C语言中,可变参数的数据读取,是由可变参数变量来完成的,这是个不常见的数据类型,说明方法是:

va_list 可变参数变量名称;

比如:

va_list ap;

说明了一个可变参数变量ap,要读这些参数,首先要说明从哪个参数开始读,如果我们打算从参数m后读,就是:

va_start(ap,m);

这样就能用下面的语句读到参数m以后各个参数的值,假如都是整数的话就是:

n=va_arg(ap, int);

一个完整的范例如下:

#include 
#include 
void vFun(char ch,int m, ...)
{
va_list ap;
int n,j;
//读固定参数部分
printf("%c\n",ch);

//从参数m后读可变参数部分
va_start(ap,m);
for (j=0;jdim=5;

对于每维的大小,要动态申请内存,就是:

A->bounds=(int *)malloc(sizeof(int)*A->dim);

在这里插入图片描述然后让:

A->bounds[0]=2; A->bounds[1]=3; A->bounds[2]=4; A->bounds[3]=5; A->bounds[5]=6;

如同上表所示。

计算维单位

再次申请存储空间、准备计算每维的单位数据个数

A->constants=(int *)malloc(dim*sizeof(int));

计算各个维的单位值:

在这里插入图片描述就是:

A->constants[4]=1;
A->constants[3]= A->bounds[4]* A->constants[4]; 	 就是6*1
A->constants[2]= A->bounds[3]*A->constants[3];	     就是5*6*1
A->constants[1]= A->bounds[2]*A->constants[2]; 	 就是4*5*6*1
A->constants[0]= A->bounds[1]*A->constants[1]; 	 就是3*4*5*6*1

申请数据存储空间

对数组A写成C语言格式,就是:A[2][3][4][5][6],这个数组总体需要存储空间大小就是:

23456*sizeof(类型)

如果类型是int、并且是在VC下,则就是:

23456sizeof(int)= 234564字节

则申请内存的语句就是:

elemtotal=1;
for(i=0;idim;i++)
	elemtotal= elemtotal*A->dim[i]
A->base=(int *)malloc(elemtotal*sizeof(int));

就是:

在这里插入图片描述我们的编程中,构造的数组并不是简单的int,而是一个很复杂的表ElemType,所以根据这个表,我们整体的数组构造函数如下表:

//必须是可变参数函数,第一个数据是维数
struct Array * Initarray(int dim, ...)
{
struct Array *A;
va_list ap;
int m,i,elemtotal;//elemtotal是数据个数
//读维数,比如InitArray(4,2,4,6,8),则先读4
va_start(ap,dim);
if (dimdim=dim;//写进维数
A->bounds=(int *)malloc(dim*sizeof(int));//申请空间,准备存储2、4、6、8
elemtotal=1;
//读每维的数据个数,如是:
//InitArray(4,2,4,6,8),则读4次,读2、4、6、8
for (i=0;ibounds[i]=m;//A->bounds逐次写进2、4、6、8
	elemtotal*=m;  //乘积,算总共有多少个数据,就是2*4*6*8
	}
va_end(ap);
//按总容量申请内存
A->base=(struct ElemType *)malloc(elemtotal*sizeof(struct ElemType));
//申请存储空间、准备计算每维的单位数据个数
A->constants=(int *)malloc(dim*sizeof(int));
//维单位的最后一个总是1,就是每列1个数据:A->constants[3]=1
A->constants[dim-1]=1;
//然后做维数乘积,计算当前维单位,其中A->bounds[]={2,4,6,8};
// A->constants[2]= A->bounds[3]*A->constants[3];=>8*1
// A->constants[1]= A->bounds[2]*A->constants[2];=>6*8*1
// A->constants[0]= A->bounds[1]*A->constants[1];=>4*6*8*1
//就是:
for (i=dim-2;i>=0;i--)
	A->constants[i]=A->bounds[i+1]*A->constants[i+1];
//所有Array表项填写完毕
return A;
}

上述过程即初始化一个数组。

5 获得数组中指定下标元素的位置:

这个问题就如同:int a[3][4][5][6],其中a[1][2][3][4]这个数据在哪里存储着?

因为在初始化这个数组的时候,有维单位数组:

constants[4]={120,30,6,1}

所以数据a[1][2][3][4]应该在:

LOC(a[0][0][0][0])+1* constants[0]+2* constants[1]+3* constants[2]+4* constants[3];

就是:

LOC(a[0][0][0][0])+1120+230+36+41

对LOC(a[0][0][0][0]),程序中就是a->base中。

此处不考虑LOC(a[0][0][0][0])的地址,仅仅计算这个偏移位置,就是:

int Local(struct Array *p,int n, ...)
{
int i,off=0;
va_list ap;

va_start(ap,n);
for (i=0;idim;i++)
	{
	off+=p->constants[i] * n;
	n=va_arg(ap,int);
	}
va_end(ap);
return off;
}

如有:

struct Array *a;
int n;
a=Initarray(4,3,4,5,6);
n=Local(a,1,2,3,4);

这个函数可以获得该数据的位置。同样的道理,也可以获得这个数据存储的地址,有了这个地址,无论读还是写,都是很容易实现的。Ar1.c就是这样的程序范例。

6 数组元素的读写

这个问题就如同:int a[3][4][5][6],求x=a[1][2][3][4]这个数据在哪里存储着。

读数组数据函数如下,实际和Local()非常相似。

struct ElemType Value(struct Array *p,int n,...)
{
struct ElemType e,*pe;
va_list ap;

int i,off=0;

va_start(ap,n);
for (i=0;idim;i++)
	{
	off+=p->constants[i] * n;
	n=va_arg(ap,int);
	}
va_end(ap);
pe=p->base;

for (i=0;iconstants[i]*n;
	n=va_arg(ap,int);
	}
va_end(ap);
pe=p->base;
for (i=0;iData=e.Data;
}

有了这些函数后,整体测试函数见ar1.c,此处不再介绍。这个程序仅仅测试了一个二维数组,实际上可以适合任意维数。

7 稀疏矩阵

稀疏矩阵没什么很明确的定义,基本就是说有大量0的矩阵。这样的矩阵直接存储、将有大量同样的数据存储着、会占用很大的存储空间。所以,压缩这些数据是很有必要的,在随后的介绍中,我们还将介绍一种基于二叉树的数据压缩方法,这里介绍一种简单的、很有针对性的数据压缩方法的实现。 对一个矩阵:

在这里插入图片描述可以设计成下面的表格进行计算:

在这里插入图片描述这样的表格存储稀疏矩阵,将能大大减少存储量,如果矩阵的数据是双精度的,则节省的存储空间要更多一些。

表1的设计、如果用C语言表述,就是:

struct ElemType
{
	int D;
};
struct Triple
{
	int i,j;
	struct ElemType e;
};

注意这里没设计成:

struct Triple
{
	int i,j;
	int D;
};

我们把数据专门设计成一个ElemType类型的表,则表明矩阵实际可以是任何类型,仅仅修改这个表中的数据类型,将基本满足大多情况下的需求。而后者则受限制很多。上面的设计中,Triple说明了表1中的一行,整个表格就是:

struct TSMatrix
{
	struct Triple *data;
	int nu,mu,tu;
};

struct Triple *data;是表格数据的首地址;

int nu,mu,tu;分别是数据的行、列、以及总数据个数。现在我们就编写一个压缩稀疏矩阵的程序。首先,我们要编写一个初始化函数CreatSMatrix(),实际就是填写下面的表格:

在这里插入图片描述 如上例的矩阵A,有6行7列,于是有:

在这里插入图片描述 统计非0元素的个数,这个程序很简单,就是: int I,j,sz=0; for (i=0;idata; for (i=0;ij=j; pe->e.D=pA[i][j]; pe++; } return p; }

读写函数是简单的,请同学们自己补充读写函数。程序ar2.c是这个函数的测试程序。

附: 线性方程组的求解程序分析

线性方程的求解过程,典型的方法是高斯消元法法。我们这里要用计算机求解,首先是要针对任意规模的线性方程,而不仅仅是常规的3、4元方程。当然,算法分析阶段还是简单的方程为例。

在这里插入图片描述 这个式子写成矩阵表达式,就是:

在这里插入图片描述 计算这个式子,是一个典型的表格加工过程,首先是有表格:

在这里插入图片描述 这个表格如果写成矩阵,在线性代数里称为增广矩阵。

逐行消元就是先把第2行第1列通过加减消元,也就是第1行所有列乘5,加第2行各列,于是有: 在这里插入图片描述用样的手段处理第3行,就是第1行乘1/2后、减第3行,结果就是: 在这里插入图片描述

根据上面的结果,再次消元,目的是将第3行第2列-7变成0,于是就是将第3行各列都乘48/7,与第2行加,结果就是: 在这里插入图片描述 这样,就是把原式逐步消减为下面的式子:

这个式子计算X3非常容易,然后逐个求解其他未知变量即可。

Ar3.c是一个多元方程组的计算程序,它会从d.txt中读到原始的方程组系数数据,修改这个文件中的数据,就能计算相应的多元一次方程。关于这个程序的全部运行过程不再介绍。

8 作业:

1 设计一个压缩多维稀疏矩阵的程序; 2 程序见ar3.c,自己设计表格、画出这个程序解上述方程组的整个运行过程。

关注
打赏
1665586602
查看更多评论
立即登录/注册

微信扫码登录

0.0829s