BDS之數組與矩陣

數組

數組是將相同數據類型的元素按照一定順序排列而成的集合。由於存儲單元是一維結構,而數據是多維的結構,則用一組連續存儲單元存放數組元素就有個次序約定問題,這也就是數組結構產生的原因。

下面我們看下簡單的數組是如何實現的:

#define MAX_ARRAY_DIM  8

#define ERROR -1
#define OK   0
#define UNDERFLOW 4
#define OVERFLOW  -2

typedef int Status;

struct Array
{
	ElemType* base; //數組元素基址 
	int dim;  //數組維數
	int *bounds; //數組維界基址
	int *constants;//數組映像函數常量基址
};

//init array
Status init_array(Array &A,int dim,...)
{
	int elemtotal = 1,i;
	va_list ap;
	if(dim<1 || dim>MAX_ARRAY_DIM)
		return ERROR;
	A.dim = dim;
	A.bounds = (int*)malloc(dim*sizeof(int));
	if(!A.bounds)
		return ERROR;
	va_start(ap,dim);
	for(i=0;i<dim;i++){
		A.bounds[i] = va_arg(ap,int);
		if(A.bounds[i]<0)
			return UNDERFLOW;
		elemtotal *=A.bounds[i];
	}
	va_end(ap);
	A.base = (ElemType*)malloc(elemtotal*sizeof(ElemType));
	if(!A.base)
		return ERROR;
	A.constants = (int*)malloc(dim*sizeof(int));
	if(!A.constants)
		return ERROR;
	A.constants[dim-1] = 1;
	for(i=dim-2;i>=0;i--)
		A.constants[i]= A.constants[i+1]*A.bounds[i+1];
	return OK;
}

void destroy_array(Array &A)
{
	if(A.base)
		free(A.base);
	if(A.bounds)
		free(A.bounds);
	if(A.constants)
		free(A.constants);
	A.base = A.bounds = A.constants = NULL;
	A.dim = 0;
}

Status locate(Array A,va_list ap,int &off)
{
	int i,idx ;
	off = 0;
	return OK;
}

Status value(ElemType &e,Array A,...)
{
	va_list ap;
	int i,off=0,idx;
	va_start(ap,A);	
	for(i = 0;i<A.dim ;i++){
		idx = va_arg(ap,int);
		if(idx<0 || idx >=A.bounds[i])
			return ERROR;
		off += A.constants[i]*idx;
	}
	e = *(A.base+off);
	return OK;
}

Status assign(Array &A,ElemType e,...)
{
	va_list ap;
	int i,idx,off=0;
	va_start(ap,e);
	for(i = 0;i<A.dim ;i++){
		idx = va_arg(ap,int);
		if(idx<0 || idx >=A.bounds[i])
			return ERROR;
		off += A.constants[i]*idx;
	}
	*(A.base+off)=e;
	return OK;
}

我們知道數組支持隨機存儲訪問,那麼對於數組結構,訪問元素的形式纔是需要關注的重點,對於一個n維的矩陣,每一維大小爲b1,b2,...bn,那麼如果給定索引j1,j2,...jn,那麼要取得該元素,就需要求其在數組存儲單元中的偏移量LOC[j1,j2,...jn]爲


矩陣

矩陣是一個數學概念,但是它在計算機中的表現形式是基於二維數組的。這裏我們主要關注稀疏矩陣,所謂稀疏矩陣是指在矩陣中非零元素較少,且分佈沒有規律。更爲準確的描述是假設m×n的矩陣中,有t個元素不爲零。令δ=t/(m×n),稱δ爲矩陣的稀疏因子,通常認爲δ<=0.05時稱爲稀疏矩陣。

下面是基於三元組順序表的稀疏矩陣壓縮存儲方式:

typedef int Status ;
#define ERROR -1;
#define OK  0;
//稀疏矩陣的三元組順序表存儲結構
#define MAX_SIZE  100 
struct Triple
{
	int i,j;//行、列索引
	ElemType e;
};
struct TSMatrix
{
	Triple data[MAX_SIZE+1];//data[0]未用
	int mu,nu,tu;//行數、列數、非零元素數
};

Status create_matrix(TSMatrix &M)
{
	int i;
	Triple T;
	Status k;
	printf("請輸入矩陣的行數、列數以及非零元素個數:");
	scanf("%d,%d,%d",&M.mu,&M.nu,&M.tu);
	if(M.tu>MAX_SIZE)
		return ERROR;
	M.data[0].i = 0;
	for(i=1;i<=M.tu;i++){
		do{
			printf("請按行序順序輸入第%d個非零元素所在的行(1~%d),列(1~%d),元素值:",i,M.mu,M.nu);
			scanf("%d,%d,%d",&T.i,&T.j,&T.e);
			k=0;
			if(T.i<1 || T.i>M.mu || T.j<1 || T.j>M.nu)
				k=1;
			if(T.i<M.data[i-1].i || T.i==M.data[i-1].i &&T.j<=M.data[i-1].j)
				k=1;
		}while(k);
		M.data[i]=T;
	}
	return OK;
}

int comp(int c1,int c2)
{
	if(c1<c2)
		return -1;
	if(c1==c2)
		return 0 ;
	return 1;
}

Status add_matrix(TSMatrix M,TSMatrix N,TSMatrix &Q)
{
	int m=1,n=1,q=0;
	if(M.mu!=N.mu || M.nu!=N.nu)
		return ERROR;
	Q.mu=M.mu;
	Q.nu=M.nu;
	while(m<=M.tu && n<=N.tu){
		switch(comp(M.data[m].i,N.data[n].i)){
			case -1:
				Q.data[++q]=M.data[m++];
				break;
			case 0:
				switch(comp(M.data[m].j,N.data[n].j)){

					case -1: 
							Q.data[++q]=M.data[m++];
							break;
					case 0 :										
							Q.data[++q]=M.data[m++];
							Q.data[q].e +=N.data[n++].e;
							if(Q.data[q].e==0)
								q--;
							break;							
					case 1:Q.data[++q]=N.data[n++];
							break;
				}
				break;
			case 1:
				Q.data[++q]=N.data[n++];
				break;
		}		
	}
	while(m<=M.tu)
		Q.data[++q]=M.data[m++];
	while(n<=N.tu)
		Q.data[++q]=N.data[n++];
	if(q>MAX_SIZE)
		return ERROR;
	Q.tu=q;
	return OK;
}

Status sub_matrix(TSMatrix M,TSMatrix N,TSMatrix &X)
{
	int i;
	if(M.mu!=N.mu || M.nu!=N.nu)
		return ERROR;
	for(i=1;i<=N.tu;++i)
		N.data[i].e*=-1;
	add_matrix(M,N,X);
	return OK;
}
//轉置
void transpose(TSMatrix M,TSMatrix &T)
{
	int p,col,q=1;
	T.mu = M.nu;
	T.nu = M.mu;
	T.tu = M.tu;
	if(T.tu){
		for(col=1;col<=M.nu;++col)
			for(p=1;p<=M.tu;++p)
				if(M.data[p].j==col){
					T.data[q].i=M.data[p].j;
					T.data[q].j=M.data[p].i;
					T.data[q++].e=M.data[p].e;
				}
	}
}

//快速轉置
Status fast_transpose(TSMatrix M,TSMatrix &T)
{
	int p,q;
	int col,t;
	T.mu = M.nu;
	T.nu = M.mu;
	T.tu = M.tu;
	int* num = (int*)malloc((M.nu+1)*sizeof(int));
	if(!num)
		return ERROR;
	int* cpot=(int*)malloc((M.nu+1)*sizeof(int));
	if(!cpot)
		return ERROR;
	if(T.tu){
		for(col=1;col<=M.nu;++col)
			num[col]=0;
		for(t=1;t<=M.tu;++t)
			++num[M.data[t].j];
		cpot[1]=1;
		for(col=2;col<=M.nu;++col)
			cpot[col]=cpot[col-1]+num[col-1];
		for(p=1;p<=M.tu;++p){
			col=M.data[p].j;
			q=cpot[col];
			T.data[q].i=M.data[p].j;
			T.data[q].j=M.data[p].i;
			T.data[q].e=M.data[p].e;
			++cpot[col];
		}
	}
	free(num);
	free(cpot);
	return OK;
}
void copy_matrix(TSMatrix M,TSMatrix &T)
{
	T=M;
}

void destroy_matrix(TSMatrix &M)
{
	M.mu=M.nu=M.tu = 0;

}

Status mult_matrix(TSMatrix M,TSMatrix N,TSMatrix &Q)
{
	int i,j,p,q;
	ElemType Qs;
	TSMatrix T;
	if(M.nu!=N.mu)
		return ERROR;
	Q.mu=M.mu;
	Q.nu=N.nu;
	Q.tu=0;
	transpose(N,T);
	for(i=1;i<=Q.mu;i++){
		q=1;
		for(j=1;j<=T.mu;j++){
			Qs=0;
			p=1;
			while(M.data[p].i<i)
				p++;
			while(T.data[q].i<j)
				q++;
			while(p<=M.tu && q<=T.tu&&M.data[p].i==i && T.data[q].i==j){
				switch(comp(M.data[p].j,T.data[q].j)){
					case -1:
						p++;
						break;
					case 0:
						Qs+=M.data[p++].e*T.data[q++].e;
						break;
					case 1:
						q++;
					break;
				}
				if(Qs){
					if(++Q.tu>MAX_SIZE)
						return ERROR;
					Q.data[Q.tu].i=i;
					Q.data[Q.tu].j=j;
					Q.data[Q.tu].e=Qs;
				}
			}
		}
	}
	return OK;
}

void print_matrix(TSMatrix M)
{
	int i,j,k=1;
	Triple *p=M.data+1;
	for(i=1;i<=M.mu;i++){
		for(j=1;j<=M.nu;j++){
			if(k<=M.tu && p->i==i &&p->j==j){
				printf("%3d",(p++)->e);
				k++;
			}else{
				printf("%3d",0);
			}
		}
		printf("\n");
	}
}

這裏,我們看看矩陣轉置的操作。矩陣的轉置是將矩陣中元素的行列位置進行互換。這裏我們討論稀疏矩陣的轉置,對於一個矩陣A,如:

1  0  2  0
0  3  0  4
0  0  5  0
其三元組的順序表存儲形式如下:
 i   j   e
0	     [0]
1  1  1  [1]
1  3  2  [2]
2  2  3  [3]
2  4  4  [4]
3  3  5  [5]
...      ...
mu=3,nu=4,tu=5
這裏需要注意的是,非零值按照一定的序列排序,轉置過程需要保證非零元素的順序,下面是矩陣轉置的最簡單的實現:
//將M轉置
transport(MAtrix M,MAtrix &X):
begin:
	X.mu = M.nu;
	X.nu = M.mu;
	X.tu = M.tu;
	if 存在非零元素
	   for (i=1;i<M.nu;i++):
	     do
	      對於每個非零元素e
		if e的列號爲當前矩陣的行號
			將e的轉置爲X中的非零元素
end

這個算法由於每次都要遍歷每個非零元素,算法複雜度爲O(nu*tu)。

考慮,轉置的關鍵問題在於,將i,j進行調換後將元素放置到data中合適的位置,如果我們能預先知道每一列(T中每一行)中的第一個非零元素在data中的位置,那麼在將元素做轉置操作時,便可直接放到data中的合適位置上去,爲了求取每一列中第一個非零元素的位置,需要先求得每一列非零元素的個數。這裏定義兩個向量num和cpot,num[col]表示矩陣中第col列中非零元的數目,cpot[col]表示M中第col列的第一個非零元在data中的恰當位置。顯然有

cpot[1]=1;

cpot[col]=cpot[col-1]+num[col-1]   2<=col<=M.nu

快速轉置的算法實現如下:

fast_transpose(Matrix M,Matrix &X)
begin:
    X.mu=M.nu;
    X.nu=M.mu;
    X.tu=M.tu;
   if 存在非零元素
	for i=1->M.nu
		初始化num向量
	for j=1->M.tu
		求取每列中非零元素個數
	cpot[1]=1;
        for col=2->M.nu 
		求每列第一個非零元在data中的序號cpot向量
        for p=1->M.tu
		do:
			取得非零元素的列號j;
			通過cpot 取得第j列的第一個非零元素在data中的序號q
			M.data[p].j ->X.data[q].i
			M.data[p].i->X.data[q].j
			M.data[p].e->X.data[q].e
			第j列的第一個非零元素在data中序號遞增
end




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