这一章只要理解,不作深入要求
数组顺序存储结构以及一些操作
#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 + 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; }
这一章节需要理解,知道其算法核心思想。