第五章 数组和广义表

抽象数据类型的数组的顺序表示和实现

n维数组的数据元素存储位置的计算公式
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>  //提取宏va_start,va_arg,va_end;用于存储变长参数表
#define MAX_ARRAY_DIM 8 //假设数组维数最大为8
#define ElemType int
#define Status int
typedef struct {
    ElemType * base;    //数组元素基址,由InitArray分配一个大小为所有元素的空间。
    int dim;        //数组维数
    int * bounds;   //数组维界基址,由InitArray分配。存储各维长度即变长参数内容
    int * constants;    //数组映像函数常量基址,由InitArray分配,计算得出。存储Ci(1<=i<=n)
}Array;

Status InitArray(Array &A, int dim,...);//若维数dim和随后的各维长度合法,构造相应的数组A,并返回1
Status DextoryArray(Array &A);
Status Value(Array A, ElemType &e,...);//A是n维数组,e为元素变量,随后是n个下标值。若下标不越界,则将指定的A的元素的值赋值给e。就是取数函数
Status Assign(Array &A, ElemType e,...);//A是n维数组,e为元素变量,随后是n个下标值。若下标不越界,则将e的值赋值给所指定的A的元素。就是给数组赋值函数。

Status InitArray(Array &A, int dim,...){
    if(dim<1 || dim>MAX_ARRAY_DIM) return 0;
    A.dim = dim;
    A.bounds = (int *)malloc(dim*sizeof(int));

    int elemtotal = 1;  //A的元素总数
    va_list ap; //存放变长参数表信息的数组
    va_start(ap,dim);
    for(int i=0;i<dim;i++){
        A.bounds[i] = va_arg(ap,int);   //存放各维长度即变长参数的内容
        if(A.bounds[i]<0) return 0;
        elemtotal *= A.bounds[i];
    }
    va_end(ap);

    A.base = (ElemType *)malloc(elemtotal*sizeof(ElemType));
    A.constants = (int *)malloc(dim*sizeof(int));//存放映像函数的常数Ci
    A.constants[dim-1] = 1;     //Cn = L = 1;指针的增减以元素大小为单位
    for(int i=dim-2;i>=0;--i)
        A.constants[i] = A.bounds[i+1] * A.constants[i+1];  //C(i-1) = Bi * Ci  1<i<=n
    return 1;
}
Status DestroyArray(Array &A){
    if(!A.base || !A.bounds || !A.constants) return 0;
    free(A.base); A.base = NULL;
    free(A.bounds); A.bounds = NULL;
    free(A.constants); A.constants = NULL;
    return 1;
}
Status Locate(Array A, va_list ap, int &off){
    //若ap指示的下标合法,求出该元素在A中的相对位置
    off = 0;
    for(int i=0;i<A.dim;i++){
        int ind = va_arg(ap,int);
        if(ind<0 || ind>=A.bounds[i]) return 0;
        off += ind*A.constants[i];  // Ji*Ci(0<=i<dim)
    }
    return 1;
}
Status Value(Array A, ElemType &e, ...){
    //取数
    va_list ap;
    va_start(ap,e);
    int result,off;
    if( (result=Locate(A,ap,off))<0 ) return result;
    e = A.base[off];
    return 1;
}
Status Assign(Array &A, ElemType e, ...){
    //赋值给数组
    va_list ap;
    va_start(ap,e);
    int result,off;
    if( (result=Locate(A,ap,off))<0 ) return result;
    A.base[off] = e;
    return 1;
}

int main(){
    Array A;
    int dim1=2,dim2=3,dim3=2,e;
    InitArray(A,3,dim1,dim2,dim3);
    for(int i=0;i<dim1;i++)
        for(int j=0;j<dim2;j++)
            for(int k=0;k<dim3;k++){
                scanf("%d",&e);
                Assign(A,e,i,j,k);
            }
    for(int i=0;i<dim1;i++)
        for(int j=0;j<dim2;j++)
            for(int k=0;k<dim3;k++){
                Value(A,e,i,j,k);
                printf("%d ",e);
            }

    return 1;
}
矩阵的压缩存储

对称矩阵、下(上)三角矩阵、三对角矩阵;稀疏矩阵
三元组顺序表:矩阵转置,快速转置
行逻辑链接的顺序表:稀疏矩阵相乘

#include<stdio.h>
#define MAXSIZE 50      //非零元个数的最大值#
#define MAXRC 20        //行数的最大值
typedef int ElemType;
typedef struct{
    int i,j;    //1<=i<=mu;1<=j<=nu
    ElemType e;
}Triple;
typedef struct {
    Triple data[MAXSIZE+1]; //0号位置弃用.以行序为主序顺序存储
    int mu,nu,tu;   //行数,列数,非零元个数
}TSMatrix;

/*
 * 矩阵转置
 * 待转置矩阵M,转置后矩阵T
 * 为了得到T的每一行的非零元素,需要对M按列进行搜索。即扫描M的稀疏矩阵n遍,每一遍可以找到M的某一列的非零元素即T的某一行的非零元素。
 * 复杂度是O(tu*nu)
 * 如果矩阵不够稀疏,tu和mu*nu同等数量级,复杂度就变成了O(mu*nu*nu)
 * 所以只适用于tu<<mu*nu
 */
int TransposeSMatrix(TSMatrix M, TSMatrix &T){
    T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
    if(!T.tu) return 0;
    int q=1;
    for(int col=1;col<=M.nu;col++)
        for(int j=1;j<=M.tu;j++)
            if(M.data[j].j==col){
                T.data[q].i = M.data[j].j;
                T.data[q].j = M.data[j].i;
                T.data[q].e = M.data[j].e;
                q++;
            }
    return 1;
}
/*
 * 另一种转置方法:快速转置
 * 设置num[col]表示M的第col列的非零元的个数
 * 设置cpot[col]表示M的第col列的第一个非零元在T.Number[]中的位置
 * 时间复杂度为O(tu+nu).如果tu和mu*nu同等数量级,复杂度就变成了O(mu*nu),和经典算法的复杂度相同
 */
int FastTransposeSMatrix(TSMatrix M, TSMatrix &T){
    T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
    if(!T.tu) return 0;
    int num[MAXSIZE]={0},cpot[MAXSIZE];
    for(int i=1;i<=M.tu;i++) num[M.data[i].j]++;
    cpot[1]=1;
    for(int col=2;col<=M.nu;col++) cpot[col] = cpot[col-1]+num[col-1];

    for(int t=1;t<=M.tu;t++){
        int col = M.data[t].j;
        T.data[cpot[col]].i = M.data[t].j;
        T.data[cpot[col]].j = M.data[t].i;
        T.data[cpot[col]].e = M.data[t].e;
        cpot[col]++;
    }
    return 1;
}
void InitMatrix(TSMatrix &M, int a, int b){
    M.mu = a; M.nu = b;
    ElemType e;
    int p=0;
    printf("Init(%d*%d):\n",a,b);
    for(int i=1;i<=M.mu;i++)
        for(int j=1;j<=M.nu;j++){
            scanf("%d",&e);
            if(!e) continue;
            p++;
            M.data[p].i = i;
            M.data[p].j = j;
            M.data[p].e = e;
        }
    M.tu = p;
    printf("\n");
}
void print(TSMatrix M){
    for(int i=1;i<=M.tu;i++)
        printf("%d %d %d\n",M.data[i].i,M.data[i].j,M.data[i].e);
    printf("\n");
}


/* 行逻辑链接的顺序表
 *
 * 三元组顺序表又称有序的双下标法,它的特点是,非零元在表中按行有序存储,这样便于进行依行顺序处理的矩阵运算。
 * 但是,若是按行号存取某一行的非零元,则需从头开始查找。
 * 所以,可以可将快速转置中的辅助数组cpot固定在稀疏矩阵的存储结构中。
 * 称这种“带行链信息”的三元组表为 行逻辑链接的顺序表
 * 描述如下
 */
typedef struct {
    Triple data[MAXSIZE+1]; //0号位置弃用.以行序为主序顺序存储
    int mu,nu,tu;           //行数,列数,非零元个数
    int rpos[MAXRC+1];      //各行第一个非零元的位置表
}RLSMatrix;
/*
 * 矩阵相乘
 * 1经典思路:笨办法.M是m1*n1,N是m2*n2,且n1=m2.复杂度O(m1*n1*n2)
 * 2矩阵是行逻辑链接存储表示,借用辅助数组rpos
 *      时间复杂度( O(M.tu*N.tu/N.mu+M.nu*N.nu) )
 */
//使用三元组相乘
int MultSMatrix(RLSMatrix M, RLSMatrix N, RLSMatrix &Q){
    if(M.nu != N.mu) return 0;
    Q.mu = M.mu; Q.nu = N.nu; Q.tu=0;
    if(M.tu * N.tu == 0) return 0;      //M和N存在零矩阵
    for(int arow=1; arow<=M.mu; arow++){
        int ctemp[MAXRC]={0};        //临时存储Q当前行的元素,最后压缩存储成三元组
        Q.rpos[arow] = Q.tu+1;      //Q的辅助数组
        int tp=0,t=0;   //分别记录M和N的当前行的最后一个非零元的位置+1
        tp=(arow<M.mu)?M.rpos[arow+1]:M.tu+1;
        for(int p=M.rpos[arow];p<tp;p++){   //找出当前行所有元素在三元组中的位置
            int brow=M.data[p].j;      //对于M当前行的每一个非零元,找出所在列brow,依据此列找N中的该行所有数
            t = brow < N.mu ? N.rpos[brow + 1] : N.tu + 1;  //找出N的brow行的数在三元组中的末尾位置
            for(int q=N.rpos[brow];q<t;q++){    //循环N的brow行的元素
                int col = N.data[q].j;
                ctemp[col] += M.data[p].e * N.data[q].e;
            }
        }//求出了Q的arow行的元素
        //压缩存储Q当前行
        for(int col=1;col<=Q.nu;col++)
            if(ctemp[col]){
                if(++Q.tu>MAXSIZE) return 0;
                Q.data[Q.tu] = {arow,col,ctemp[col]};
            }
    }
    return 1;
}
void InitRLSMatrix(RLSMatrix &M, int a, int b){
    M.mu = a; M.nu = b;
    ElemType e;
    int p=0;
    M.rpos[0]=1;
    int sign,num[MAXRC+1]={0};
    printf("Init(%d*%d):\n",a,b);
    for(int i=1;i<=M.mu;i++){
        num[i]=0;
        sign=0;
        for(int j=1;j<=M.nu;j++){
            scanf("%d",&e);
            if(!e) continue;
            p++;
            M.data[p].i = i;
            M.data[p].j = j;
            M.data[p].e = e;
            if(sign==0){
                M.rpos[i]=p;
                sign=1;
            }
            num[i]++;
        }
        if(sign==0) M.rpos[i] = M.rpos[i-1]+num[i-1];     //防止某一行没有非零元
    }
    M.tu = p;
    printf("\n");
}
void print(RLSMatrix M){
    for(int i=1;i<=M.tu;i++)
        printf("%d %d %d\n",M.data[i].i,M.data[i].j,M.data[i].e);
    printf("\n");
}

int main(){
//    TSMatrix M,T;
//    InitMatrix(M,6,7);
//    print(M);
//    TransposeSMatrix(M,T);
//    print(T);
//
//    FastTransposeSMatrix(T,M);
//    print(M);

    RLSMatrix M,T,Q;
    InitRLSMatrix(M,3,4);
    InitRLSMatrix(T,4,2);
    MultSMatrix(M,T,Q);
    print(Q);
    return 1;
}
/*
0 12 9 0 0 0 0
0 0 0 0 0 0 0
-3 0 0 0 0 14 0
0 0 24 0 0 0 0
0 18 0 0 0 0 0
15 0 0 -7 0 0 0
 */
/*
3 0 0 5
0 -1 0 0
2 0 0 0

0 2
1 0
-2 4
0 0
 */
十字链表
广义表

暂时不写,先写后面的树、图

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在学C++

2角不嫌多,1角不嫌少

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值