数据结构 - 数组、矩阵、广义表存储

来源:互联网 发布:linux touch 多个文件 编辑:程序博客网 时间:2024/05/17 22:54

数组的定义

  1. 数组的定义
    数组是下标index 和值value 组成的序对的集合。
    在数组中,每个有定义的下标都与一个值对应,这个值称做数组元素。
    每个序对形如: (index,value)

数组的顺序表示和实现

由于计算机的内存结构是一维的,因此用一维内存来表示多维数组,就必须按某种次序将数组元素排成一列序列,然后将这个线性序列存放在存储器中。
一般都是采用顺序存储的方法来表示数组

  1. 一维数组的顺序表示
    设第一个数组元素a[0]的存储地址是loc(a[0]),若已知每个数组元素占k个存储单元,则下标为i的数组元素a[i]的存储地址loc(a[i])为
    loc(a[i])=loc(a[0])+i*k
    i=0,1,2,…,n-1。

  2. 二维数组的顺序表示
    二维数组a[m][n]映射到一维的存储空间时有两种顺序:行优先和列优先。
    大多数语言如PASCAL、BASIC、C、C++等都是按行优先顺序存储的,FORTRAN是按列优先顺序存储的。

矩阵的压缩存储

矩阵的存储——二维数组
考虑:若矩阵阶数很高,且有许多值相同的元素或零元素,怎么提高存储效率?
特殊矩阵——值相同的元素或者零元素在矩阵中的分布有一定规律
稀疏矩阵——矩阵中有大量零元素,非零元素比较少。
压缩存储:为多个相同的非零元素只分配一个存储空间;对零元素不分配空间。

对称矩阵的压缩存储

ij,则aij 在下三角矩阵中。 aij 之前的i-1行(从第1行到第i-1行)一共有1+2+…+i-1=i(i-1)/2个元素,在第i行上, aij 之前恰有j-1个元素(即ai0,ai1,ai2,…,aij-1),因此有:               k=i*(i-1)/2+j-1iji<j,则aij 是在上三角矩阵中。因为aij=aji,所以只要交换上述对应关系式中的ij即可得到:              k=j*(j-1)/2+i-1i<j    根据上述的下标对应关系,对于矩阵中的任意元素aij,均可在一维数组sa中唯一确定其位置k;反之,对所有k=0,1, …, n(n+1)/2-1,都能确定sa[k]中的元素在矩阵中的位置(i,j)。

三角矩阵的压缩存储

三角矩阵中的重复元素c可共享一个存储空间,其余的元素正好有n(n+1)/2个,因此,三角矩阵可压缩存储到一维数组sa[n(n+1)/2+1]中,其中c存放在数组的第1个位置(亦可放在最后一个位置)。
上三角矩阵元素aij保存在数组sa中时,下标值k与(i,j)之间的对应关系是?
下三角矩阵元素aij保存在数组sa中时,下标值k与(i,j)之间的对应关系是?

稀疏矩阵的存储

解决方法:在存储非零元素的同时,同时记下它所在的行和列的位置(i, j)。
由于三元组(i, j, aij)唯一确定了矩阵A的一个非零元。因此,稀疏矩阵可由表示非零元的三元组及其行列数唯一确定。
例如,下列三元组表
( (1,2,12), (1,3,9), (3,1,-3), (3,8,4), (4,3,24), (4,6,2), (5,2,18), (6,7,-7), (7,4,-6) ) 和行列信息(7,8,9)便可描述如图5.6所示的稀疏矩阵
注:行列信息(7,8,9)中,7:行;8:列;9:非零元个数

三元组顺序表

以顺序存储结构来表示三元组表,则得到稀疏矩阵的一种压缩存储方法——三元顺序表。

⑴ 三元组结点定义      #define MAX_SIZE 1000  typedef int elemtype ;  typedef struct{    int   row ;     /*  行下标  */      int   col ;        /*  列下标  */elemtype value;      /*  元素值  */}Triple ;
⑵  三元组顺序表定义    typedef struct {   int  rn ;         /*   行数   */int  cn;         /*   列数   */int  tn ;         /*    非0元素个数   */Triple   data[MAX_SIZE] ; }TMatrix ; TMatrix  a;//定义了一个用三元组顺序表表示的稀疏矩阵

矩阵的转置

设稀疏矩阵A是按行优先顺序压缩存储在三元组表a.data中,若仅仅是简单地交换a.data中i和j的内容,得到三元组表b.data,b.data将是一个按列优先顺序存储的稀疏矩阵B,要得到按行优先顺序存储的b.data,就必须重新排列三元组表b.data中元素的顺序。
由于A的列是B的行,因此,按a.data的列序转置,所得到的转置矩阵B的三元组表b.data必定是按行优先存放的。

转置矩阵的算法

按方法一求转置矩阵的算法如下:void TransMatrix(TMatrix a , TMatrix * b){   int i , j , col ;b->rn=a.cn ; b->cn=a.rn ; b->tn=a.tn ;/*    置三元组表b->data的行、列数和非0元素个数 */if  (b->tn==0)    printf(“ The Matrix A=0\n” );else{   j=0; //b中三元组 数组下标for  (col=1; col<=a.cn ; col++)/*   每次循环扫描 a的第col 列非零元,得到b的第col行非零元  */      for  (i=0 ;i<a.tn ; i++)  /*   循环次数是非0元素个数   */                                 if  (a.data[i].col==col)                                   {    b-> data[j].row=a.data[i].col ;                                         b-> data[j].col=a.data[i].row ;                                          b-> data[j].value=a.data[i].value;                                          j++ ;                                     }                    }  }

快速转置算法

快速转置算法如下   void  FastTransMatrix(TMatrix a, TMatrix b){    int p , q , col , k ;int num[N] , copt[N] ;  //N为矩阵列数b.rn=a.cn ; b.cn=a.rn ; b.tn=a.tn ; /*   置三元组表b.data的行、列数和非0元素个数  */ if  (b.tn==0)    printf(“ The Matrix A=0\n” ) ;else{  for (col=1 ; col<=a.cn ; ++col)    num[col]=0 ;   /*  向量num[ ]初始化为0   */               for (k=0 ; k<a.tn ; ++k)       ++num[ a.data[k].col] ;           /*   求原矩阵中每一列非0元素个数  */for (cpot[1]=0, col=2 ; col<=a.cn ; ++col)       cpot [col]=cpot[col-1]+num[col-1] ;      /*  求第col列中第一个非0元在b.data中的序号 */for (p=0 ; p<a.tn ; ++p)    {   col = a.data[p].col ;          q=cpot[col] ; //矩阵b的第col行的元素所在位置          b.data[q].row=a.data[p].col ;          b.data[q].col=a.data[p].row ;           b.data[q].value=a.data[p].value ;            ++cpot[col] ;      /*至关重要!!当本列中增加新元素时,位置即变为下一个非零元素的位置  */     }}}

十字链表(链式存储)

    对于稀疏矩阵,当非0元素的个数和位置在操作过程中变化较大时,采用链式存储结构表示比三元组的线性表更方便。   矩阵中非0元素的结点所含的域有:行、列、值、行指针(指向同一行的下一个非0元)、列指针(指向同一列的下一个非0元)。其次,十字交叉链表还有一个头结点

稀疏矩阵中同一行的非0元素的由right指针域 链接成一个行链表, 由down指针域链接成一个列链表。
每个非0元素既是某个行链表中的一个结点,同时又是某个列链表中的一个结点,所有的非0元素构成一个十字交叉的链表,称为十字链表。

此外,用两个一维数组rhead和chead分别存储行链表的头指针和列链表的头指针。
结点的描述如下:typedef struct  OLnode  {   int  row , col ;   /*  行号和列号  */     elemtype value ;    /*  元素值  */struct  OLnode  *down , *right ;} OLNode, *OLink ;   /*  非0元素结点  */typedef struct{   int   rn;        /*  矩阵的行数  */     int   cn;        /*  矩阵的列数  */int   tn;        /*  非0元素总数  */OLink  * rhead ;      //行链表头指针向量基址OLink  * chead ;    //列链表头指针向量基址} CrossList 

广义表

广义表是线性表的推广和扩充,在人工智能领域中应用十分广泛。
第2章中的线性表定义为n(n≧0 )个元素a1, a2 ,…, an的有穷序列,该序列中的所有元素具有相同的数据类型且只能是原子项(Atom)。所谓原子项可以是一个数或一个结构,在结构上不可再分。若放松对元素的这种限制,容许它们具有其自身结构,就产生了广义表的概念。
广义表(Lists,又称为列表 ):是由n(n ≧0)个元素组成的有穷序列: LS=(a1,a2,…,an) ,其中ai或者是原子项,或者是一个广义表。LS是广义表的名字,n为它的长度。若ai是广义表,则称为LS的子表。
习惯上:原子项用小写字母,子表用大写字母。
若广义表LS非空时:
◆ a1(表中第一个元素)称为表头;
◆ 其余元素组成的子表称为表尾;(a2,a3,…,an)
◆ 广义表中所包含的元素(包括原子和子表)的个数称为表的长 度。
◆ 广义表中括号的最大层数称为表深 (度)

广义表的元素可以是原子项,也可以是子表,子表的元素又可以是子表, …。即广义表是一个多层次的结构。表5-2中的广义表D的图形表示如图5-12所示。
广义表可以被其它广义表所共享,也可以共享其它广义表。广义表共享其它广义表时不必列出子表的值,而是通过表名引用。如:D=(A,B,C)
广义表本身可以是一个递归表。如:E=(a,E)
根据对表头、表尾的定义,任何一个非空广义表的表头可以是原子,也可以是子表, 而表尾必定是广义表。

广义表的存储结构

由于广义表中的数据元素具有不同的结构,通常用链式存储结构表示,每个数据元素用一个结点表示。因此,广义表中就有两类结点:
◆ 一类是表结点,用来表示广义表项,由标志域,表头指针域,表尾指针域组成;
◆ 另一类是原子结点,用来表示原子项,由标志域,原子的值域组成。如图5-13所示。
只要广义表非空,都是由表头和表尾组成。即一个确定的表头和表尾就唯一确定一个广义表。

相应的数据结构定义如下:typedef struct GLNode{  int   tag ;     /*  标志域,为1:表结点;为0 :原子结点  */union{  Atomtype atom;     /* 原子结点的值域  */struct    {  struct GLNode  *hp , *tp ;     }ptr ;   /*  ptr和atom两成员共用  */}Gdata ; } GLNode ;      /* 广义表结点类型  */
0 1
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微信设置成听筒模式怎么办 微信验证看不全怎么办 qq看视频无法放大怎么办 苹果6功能键坏了怎么办 qq显示不了头像和图片怎么办 关于学校随便更换老师家长怎么办 qq群昵称改不了怎么办 电脑qq群视频进不去怎么办 微信头像没保存怎么办 qq群信息不响怎么办 微信封面换不了怎么办 微信头像没有了怎么办 科二超过5次怎么办 蘑菇街个人中心打不开怎么办 蘑菇街的个人中心打不开怎么办 qq群500人满了怎么办 微信零钱发不出来怎么办 微信零钱发不出去怎么办 微信零钱发不了红包怎么办 当亲戚孤立你该怎么办 陌陌好友找不到了怎么办 换了手机号陌陌号找不到怎么办 微信解析包出现问题怎么办 老板找借口不发工资怎么办 苹果手机忘记id帐号密码怎么办 苹果6手机帐号密码忘记怎么办 狗狗在窝里撒尿怎么办 爸妈和媳妇不好怎么办 如果老婆不用老公的钱怎么办 老公不给老婆钱花怎么办 奶少孩子不愿意吸怎么办 孩子识字少不愿意读书怎么办 夏天孩子不愿意喝水尿量少黄怎么办 孩子不喜欢与别人沟通怎么办 异地恋他爸妈不同意怎么办 刚装修家里很臭怎么办 狗弄得家里很臭怎么办 抑郁症患者不愿意回家见家人怎么办 属猴的人养猫怎么办 对方电话拉黑我怎么办 手机号码换了淘宝账号怎么办