您的位置:首页 > 理论基础 > 数据结构算法

数据结构(C语言版 严蔚敏著)——数组和广义表

2018-03-30 09:49 513 查看
这一章只要理解,不作深入要求
数组顺序存储结构以及一些操作#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define MAX_ARRAY_DIM 8
typedef int ElemType;
typedef struct {
ElemType *base; //数组元素基址
int dim; //维数
int *bounds; //数组维界基址
int *constants; //数组映像函数常量基址
} Array;

int InitArray(Array &A, int dim, ...) {
//这里用的是“可变参”形参方式。它主要解决维数不定的问题。
//举例:设有4维数组,各维分别是:4,5,6,7(这些数字是随意给的),那么,调用方式:
//InitArray(ar, 4, 4, 5, 6, 7);
//ar其中,ar也是假设的变量名称, 4表示数组有4维, 4, 5, 6, 7这4个数是各维大小
//如果是5维的,那么就这样:
//InitArray(ar, 5, 第一维数,第二维数,第三维数,第四维数,第五维数);
//若维数dim和随后的各维长度合法,则构造相应的数组A,并返回OK。
if (dim < 1 || dim > MAX_ARRAY_DIM)
return 0;
A.dim = dim;
A.bounds = (int *) malloc(dim * sizeof(int));
if (!A.bounds)
exit(0);
//若各维长度合法,则存入A.bounds,并求出A的元素总数elemtotal。
int elemtotal = 1;
//ap为va_list类型,是存放变长参数表信息的数组。
va_list ap;
//dim表示最后一个确定的参数,把后面的参数都存在ap中
//va_start
va_start(ap, dim);
for (int i = 0; i < dim; ++i) {
//每当取出一个参数,ap指针就指向下一个
A.bounds[i] = va_arg(ap, int);
//变参不合法,返回0
if (A.bounds[i] < 0)
return 0;
//把各维度相乘,即为元素总量
elemtotal *= A.bounds[i];
}
//va_end
va_end(ap);
//分配元素空间
A.base = (ElemType *) malloc(elemtotal * sizeof(ElemType));
if (!A.base)
exit(0);
//求映像函数的常数ci,并存入A.constants[i-1],i=1,...,dim
A.constants = (int *) malloc(dim * sizeof(int));
if (!A.constants)
exit(0);
//第一维映像函数为1
A.constants[dim - 1] = 1;
//从二维开始,映像函数=上一维的大小*上一维映像函数的大小
//下面有对constants的详细解释
for (int i = dim - 2; i >= 0; --i) {
A.constants[i] = A.bounds[i + 1] * A.constants[i + 1];
}
return 1;
}

int DeatroyArray(Array &A) {
//销毁数组A。
if (!A.base)
return 0;
free(A.base);
A.base = NULL;
if (!A.bounds)
return 0;
free(A.bounds);
A.bounds = NULL;
if (!A.constants)
return 0;
free(A.constants);
A.constants = NULL;
return 1;
}

// 在定位一个数组元素的时候,比如要求a[2][2][4]元素的值,按照我们的常理,可以直接用
// a[2][2][4] = x;来赋值或者取值,但是这里定义的数组维度是可以改变的,比如说8维,
// 而且数组的空间都是用malloc函数分配得到的,基址为结构体里面的指针。
// 这时就不能再用上面的方法求值或者复制。需要得到我们要赋值或者取值的元的地址,
// 通过地址操作来进行值的获取。
// 因为是a[3][4][5],所以a[2][2][4]的地址就是base+2*4*5+2*5+4*1;为了简便,
// 在结构体中设置了constants
int Locate(Array A, va_list ap, int &off) {
//若ap指示的各下标值合法,则求出该元素在A中相对地址off
//ap指数组一系列下标,比如a[2][2][4],那么这个 va_list为(2,2,4)
off = 0;
int ind;
for (int i = 0; i < A.dim; ++i) {
ind = va_arg(ap, int);
//ind合法大小为0~A.bounds[i]-1
if (ind < 0 || ind >= A.bounds[i])
return 0;
off += A.constants[i] * ind;
}
return 1;
}

int Value(Array A, ElemType &e, ...) {
//A是n维数组,e为元素变量,随后是n个下标值
//若各下标不超界,则e赋值为所指定的A的元素值,返回1
va_list ap;
int off, result;
//va_list从确定参数e后面开始
va_start(ap, e);
result = Locate(A, ap, off);
if (result <= 0)
return result;
//基地址+相对地址=元素真正地址
e = *(A.base + off);
return 1;
}

int Assign(Array &A, ElemType e, ...) {
//A是n维数组,e为元素变量,随后是n个下标值
//若下标不超界,则将e的值赋给所指定的A的元素,并返回1
va_list ap;
va_start(ap, e);
int result, off;
result = Locate(A, ap, off);
if (result <= 0)
return 0;
*(A.base + off) = e;
return 1;
}三元组顺序表#define MAXSIZE 12500 //假设非零元个数的最大值为12500
typedef int ElemType;
typedef struct {
int i, j; //该非零元的行下标和列下标
ElemType e;
} Triple;
typedef struct {
Triple data[MAXSIZE + 1];//非零元三元组表,data[0]未用
int mu, nu, tu; //矩阵的行数、列数和非零元个数
} TSMatrix;

int TransposeSMatrix(TSMatrix M, TSMatrix &T) {
//采用三元组存储表示,求稀疏矩阵M的转置矩阵T
//时间复杂度O(nu*tu)
//此算法仅适于tu<<mu*nu的情况
T.mu = M.nu;//行数为 M的列数
T.nu = M.mu;//列数为 M的行数
T.tu = M.tu;//非零元素个数
if (T.tu) {
int q = 1;
//列循环
for (int col = 1; col <= M.nu; ++col)
//在非零元素中找列下标相等的,互换
for (int 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;
++q;
}
}
return 1;
}

int FastTransposeSMatrix(TSMatrix M,TSMatrix &T){
//采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵T
//此方法称为快速转置
//多用了两个辅助向量,算法中有4个单循环,分别为nu,tu
//总时间复杂度为O(nu+tu)
//在M的非零个数tu和mu*nu等数量级时
//时间复杂度为O(mu*nu),这时和上面的时间复杂度相同
T.mu=M.nu;
T.nu=M.mu;
T.tu=M.tu;
int col,q;
int num[T.nu+1];
int cpot[T.nu+1];
cpot[1]=1;
if(T.tu){
for(col=1;col<=M.nu;++col)
num[col]=0;//初始化列元素数组
for(int t=1;t<=M.tu;++t)
++num[M.data[t].j];//求M中每一列含非零元素个数
//求第col列中第一个非零元素在b.data中的序号
//第一列的起始序号为1
//第二列的起始序号为第一列非零元素个数加第一列起始序号
for(col=2;col<=M.nu;++col)
cpot[col]=cpot[col-1]+num[col-1];
for (int 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];
}
}
return 1;
}

行逻辑链接的顺序表#define MAXSIZE 100
#define MAXRC 100
typedef int ElemType;
typedef struct {
int i, j; //该非零元的行下标和列下标
ElemType e;
} Triple;
typedef struct {
Triple data[MAXSIZE + 1];//非零元三元组表
int rpos[MAXRC + 1]; //各行第一个非零元的位置表
int mu, nu, tu; //矩阵的行数、列数和非零元个数
} RLSMatrix;

int MultSMatrix(RLSMatrix M, RLSMatrix N, RLSMatrix &Q) {
//求矩阵乘积Q=M*N,采用行逻辑链接存储表示
//求Q的所有非零元的时间复杂度为O(M.tu*N.tu/N.mu)
//进行压缩存储的时间复杂度为O(M.mu*N.nu)
//总的时间复杂度为O(M.mu*N.nu+M.tu*N.tu/N.mu)
//矩阵乘法规则可知C(i,j) = A(i,1)*B(1,j)+A(i,2)*B(2,j)+....+A(i,n)*B(n,j),
// 即C(i,j)为A的第i行与B的第j列非零元素乘积之和。设置累加器temp[B的列值]保存
// C矩阵每行的值,结束后将temp中的值赋给C矩阵。
if (M.nu != N.mu)
return 0;
//Q初始化
Q.mu = M.mu;
Q.nu = N.nu;
Q.tu = 0;
int ctemp[N.nu + 1];
int tp, q, p, t, brow, ccol;
//Q是非零矩阵
if (M.tu * N.tu != 0) {
//处理M的每一行
for (int arow = 1; arow <= M.mu; ++arow) {
//当前行各元素累加器清零
ctemp[arow] = 0;
Q.rpos[arow] = Q.tu + 1;
if (arow < M.mu)
tp = M.rpos[arow + 1];
else
tp = M.tu + 1;
for (p = M.rpos[arow]; p < tp; ++p) {
//对当前行中每个非零元
//找到对应元在N中的行号
brow = M.data[p].j;
if (brow < N.mu)
t = N.rpos[brow + 1];
else
t = N.tu + 1;
for (q = N.rpos[brow]; q < t; ++q) {
ccol = N.data[q].j;//乘积元素在Q中列号
ctemp[ccol] += M.data[p].e * N.data[q].e;
}
}//求得Q中第arow行的非零元
for (ccol = 1; ccol <= Q.nu; ++ccol) {
//压缩存储该行非零元
if (ctemp[ccol]) {//非零元
if (++Q.tu > MAXSIZE)
return 0;
Q.data[Q.tu].i = arow;
Q.data[Q.tu].j = ccol;
Q.data[Q.tu].e = ctemp[ccol];
}
}
}
}
return 1;
}
十字链表存储结构和创建typedef int ElemType;
typedef struct OLNode {
int i, j;
ElemType e;
struct OLNode *right, *down;
} OLNode, *OLink;

typedef struct {
OLink *rhead, *chead;
int mu, nu, tu;
} CrossList;

int CreateSMatrix_OL(CrossList &M) {
//创建稀疏矩阵M,采用十字链表存储表示
int m, n, t, i, j, e;
OLink p, q;
scanf("%d%d%d", &m, &n, &t);//输入行数,列数,非零元个数
M.mu = m;
M.nu = n;
M.tu = t;
if (!(M.rhead = (OLink *) malloc((m +
a193
1) * sizeof(OLink))))
exit(0);
if (!(M.chead = (OLink *) malloc((n + 1) * sizeof(OLink))))
exit(0);
M.rhead[m + 1] = M.chead[n + 1] = NULL;//初始化行列头指针;各行列链表为空表
for (scanf("%d%d%d", &i, &j, &e); i != 0; scanf("%d%d%d", &i, &j, &e)) {//按任意次序输入非零元
if (!(p = (OLink) malloc(sizeof(OLNode))))
exit(0);
//生成结点
p->i = i;
p->j = j;
p->e = e;
if (M.rhead[i] == NULL || M.rhead[i]->j > j) {
p->right = M.rhead[i];
M.rhead[i] = p;
} else {//寻找在行表中插入位置
for (q = M.rhead[i]; (q->right) && q->right->j < j; q = q->right);
p->right = q->right;
q->right = p;//完成行插入
}
if (M.chead[j] == NULL || M.chead[j]->i > i) {
p->down = M.chead[j];
M.chead[j] = p;
} else {//寻找在列表中插入位置
for (q = M.chead[j]; (q->down) && q->down->i < i; q = q->down);
p->down = q->down;
q->down = p;//完成列插入
}
}
return 1;
}

这一章节需要理解,知道其算法核心思想。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: