《多维数组和广义表》由会员分享,可在线阅读,更多相关《多维数组和广义表(58页珍藏版)》请在金锄头文库上搜索。
1、第 6 6 章 树知 识 点多维数组的逻辑结构和存储结构特殊矩阵的压缩存储稀疏矩阵的三元组存储、十字链表存储广义表的逻辑结构、存储结构及其基本算法难点多维数组和广义表要 求 多维数组和广义表第 6 6 章 目 录6-1 6-1 多维数组 6-2 6-2 特殊矩阵的压缩存储6-3 6-3 稀疏矩阵 6-4 6-4 广义表 小 结验证性实验:稀疏矩阵和广义表子系统自主性实验6 6:稀疏矩阵十字链表的存储 单元练习6多维数组和广义表6-1 多维数组6.1.1逻辑结构数组作为一种数据结构,其特点是结构中的元素可以是具有某种结构的数据,但属于同一数据类型。比如,一维数组可以看作一个线性表,二维数组可以看
2、作“数据元素是一维数组”的一维数组,三维数组可以看作“数据元素是二维数组”的一维数组。一般把三维以上的数组称为多维数组,n维的多维数组可以视为n 1维数组元素组成的线性结构。其中每一个一维数组又由m个单元组成。图6-1是一个n行m列的数组。多维数组和广义表多维数组和广义表在二维数组中的每一个元素最多可以有两个直接前驱和两个直接后继(边界除外),在n维数组中的每一个元素最多可以有n个直接前驱和n个直接后继。所以多维数组是一种非线性结构。数组是一个具有固定格式和数量的数据有序集,每一个数据元素有唯一的一组下标来标识,通常在很多高级语言中数组一旦被定义,每一维的大小及上下界都不能改变。因此,在数组上
3、一般不做插入或删除数据元素的操作。在数组中经常做的两种操作如下。(1)取值操作:给定一组下标,读取其对应的数据元素。 (2)赋值操作:给定一组下标,存储或修改与其相对应的数据元素。多维数组和广义表6.1.2 存储结构通常,数组在内存被映象为向量,即用向量作为数组的一种存储结构,这是因为在计算机内存储结构是一维的。数组的行列固定后,通过一个映象函数,就可以根据数组元素的下标得到它的存储地址。对于一维数组只要按下标顺序分配即可;对多维数组分配时,要把它的元素映象存储在一维存储器中。1存储方式(1)以行为主(rowmajororder)以行为主的存储方式也称为按行优先顺序方式,实现时按行号从小到大的
4、顺序,先存储第0行的全部元素,再存放第1行的元素、第2行的元素一个23二维数组的逻辑结构如图6-2所示,以行为主的内存映象如图6-3(a)所示,其分配顺序为:a00,a01,a02,a10,a11,a12。多维数组和广义表多维数组和广义表以行为主序的分配规律是:最右边的下标先变化,即最右下标从小到大,循环一遍后,右边第二个下标再变,从右向左,最后是左下标。(2)以列为主序(columnmajororder)以列为主的存储方式也称为按列优先顺序方式,实现时按列号从小到大的顺序,先存储第0列的全部元素,再存储第1列的元素、第2列的元素图6-2所示的逻辑结构,以列为主的内存映象如图6-3(b)所示,
5、其分配顺序为:a00,a10,a01,a11,a02,a12。以列为主分配的规律恰好与以行为主次序相反:最左边的下标先变化,即最左下标从小到大,循环一遍后,左边第二个下标再变,从左向右,最后是右下标。多维数组和广义表2存储地址“以行为主”次序分配存储单元为例看其地址的计算(1)二维数组中aij的地址在C语言中数组中每一维的下界定义为0,数组的基址为LOC(a00),每个数组元素占据d个字节,那么aij的物理地址可用一个线性寻址函数计算:LOC(aij)=LOC(a00)+(in+j)d(0下标起始的语言)(2)三维数组中aijk的地址同理对于三维数组元素aijk的物理地址为:LOC(aijk)
6、=LOC(a000)+(inp+jp+k)d(0下标起始的语言)多维数组和广义表【例6-1】设二维数组A56,每个元素占4个字节(Byte),存储器按字节编址。已知A的起始地址为2000。计算(1)数组的大小nmd=564=120Byte(2)数组结点a45的存储地址LOC(aij)=LOC(a00)+(i*n+j)*d/n为总列数LOC(a45)=2000+(46+5)4=2116(3)按行为主存储,计算a32的存储地址LOC(aij)=LOC(a00)+(i*n+j)*d/n为总列数LOC(a32)=2000+(36+2)4=2080(4)按列为主存储,计算a32的存储地址LOC(aij)
7、=LOC(a00)+(j*m+i)*d/m为总行数LOC(a32)=2000+(25+3)4=2052多维数组和广义表 【例6-2】若矩阵A An nm m 中存在某个元素a aijij,满足:a aijij是第i i行中最小值且是第j j列中的最大值,则称该元素为矩阵A A的一个鞍点。试编写一个算法,找出A A中的所有鞍点。基本思想:在矩阵A A中求出每一行的最小值元素,然后判断该元素是否是它所在列中的最大值。如果是则打印输出,接着处理下一行。设矩阵A A用一个二维数组表示,其算法如下:voidsaddle(intA,intn,intm)inti,j,min;for(i=0;in;i+)/按
8、行处理min=Ai0for(j=1;jm;j+)if(Aijmin)min=Aij;/找第i行最小值多维数组和广义表for(j=0;jm;j+)/检测最小值是否是鞍点if(Aij=min)k=j;p=0;while(pn&Apj=n)printf(%d,%d,%dn,i,k,min);算法的时间复杂度为O O( (n n ( (m m + +n mn m)。多维数组和广义表6-2 特殊矩阵的压缩存储矩阵是一个二维数组,是众多科学与工程计算问题中研究的数学对象。在矩阵中非零元素或零元素的分布有一定规律的矩阵称为特殊矩阵,如三角矩阵、对称矩阵、带状矩阵、稀疏矩阵等。当矩阵的阶数很大时,用普通的二维
9、数组存储这些特殊矩阵将会占用很多的存储单元。从节约存储空间的角度考虑,下面来考虑这些特殊矩阵的存储方法。多维数组和广义表 6.2.1对称矩阵对称矩阵是一种特殊矩阵,n阶方阵的元素满足性质:aij=aji(0=i,j=n 1)。如图6-4所示是一个阶对称矩阵。对称矩阵是关于主对角线的对称,因此只需存储上三角或下三角部分的数据即可。比如,只存储下三角中的元素aij,其特点是中j=i且0=i=n 1,对于上三角中的元素aij,它和对应的aji相等,因此当访问的元素在上三角时,直接去访问和它对应的下三角元素即可,这样,原来需要n n个存储单元,现在只需要n(n+1)/2个存储单元了,节约了n(n 1)
10、/2个存储单元。多维数组和广义表图6-45阶对称方阵及它的压缩存储多维数组和广义表 如何只存储下三角部分呢?将下三角部分以行序为主序顺序存储到一个向量SA中去。在下三角中共有n(n+1)/2个元素,存储到一个向量空间SA0至SAn(n+1)/2- -1中,存储顺序可用图6-5示意。图6-5 6-5 对称矩阵下三角压缩存储多维数组和广义表原矩阵下三角中的某一个元素aij具体对应一个sak,用“以行优先”存放下三角部分的元素时,a00存入sa0,a10存入sa1,a11存入sa2。sak与aij的一一对应关系为:k=j(j+1)/2+i(0=kn (n+1)/2 1)当ij时,在下三角部分aij前
11、有i行,共有1+2+3+i个元素,而aij是第i行的第j个元素,即有k=1+2+3+i+j=i(i+1)/2+j。当ij时,aij是上三角中的元素,因为aij=aji,这样,访问上三角中的元素aij时则去访问和它对应的下三角中的aji即可,因此将上式中的行列下标交换就是上三角中的元素在SA中的对应关系: k=j(j+1)/2+i(0=krow=m; H-row=m; H-col=n; H-col=n; hd0=H; hd0=H; for(i=1; iS; i+) for(i=1; irow=0; p-col=0;p-row=0; p-col=0; p-right=p; p-down=p; p-
12、right=p; p-down=p; hdi=p; hdi-1-v_next.next=p; hdi=p; hdi-1-v_next.next=p; 多维数组和广义表hdS-v_next.next=H; hdS-v_next.next=H; / / 将头结点们形成循环链表 for(k=1;k=t;k+)for(k=1;krow=i; p-col=j; p-v_next.v=v p-row=i; p-col=j; p-v_next.v=v q=hdi; q=hdi; while(q-right!=hdi&(q-right-col)right!=hdi&(q-right-col)right;q=q
13、-right; p-right=q-right; / p-right=q-right; / 插入 q-right=p; q=hdi;q-right=p; q=hdi; while(q-down!=hdj&(q-down-row)down!=hdj&(q-down-row)down;q=q-down; p-down=q-down; p-down=q-down; / / 插入 q-down=p;q-down=p; return H;return H; 多维数组和广义表上述算法中,建立头结点循环链表时间复杂度为O O( (S S) ),插入每个结点到相应的行表和列表的时间复杂度是O O( (t t
14、S S) ),这是因为每个结点插入时都要在链表中寻找插入位置,所以总的时间复杂度为O O( (t t S S) )。该算法对三元组的输入顺序没有要求。如果我们输入三元组时是按以行为主序(或列)输入的,则每次将新结点插入到链表的尾部的,改进算法后,时间复杂度为O O( (S S+ +t t) )。多维数组和广义表2 2稀疏矩阵的加法 已知两个十字链表存储的稀疏矩阵A A和B B,计算C C= =A A+ +B B,C C也采用十字链表方式存储,并且在A A的基础上形成C C。 由矩阵的加法规则知,只有A A和B B行列对应相等,二者才能相加。C C中的非零元素c cijij只可能有3 3种情况:
15、或者是a aijij+ +b bijij,或者是a aijij(b bijij=0=0),或者是b bijij(a aijij=0=0),因此当B B加到A A上时,对A A十字链表的当前结点来说,对应下列四种情况:或者改变结点的值(a aijij+ +b bijij00),或者不变(b bijij 0 0),或者插入一个新结点(a aijij 0 0),还可能是删除一个结点(a aijij+ +b bijij 0 0)。整个运算从矩阵的第一行起逐行进行。对每一行都从行表的头结点出发,分别找到A A和B B在该行中的第一个非零元素结点后开始比较,然后按4 4种不同情况分别处理。设papa和pb
16、pb分别指向A A和B B的十字链表中行号相同的两个结点,4 4种情况如下:多维数组和广义表(1 1)若pa-col=pb-colpa-col=pb-col且pa-pa-v v+pb-+pb-v v0 0,则只要用a aijij+ +b bijij的值改写papa所指结点的值域即可。(2 2)若pa-col=pb-colpa-col=pb-col且pa-pa-v v+pb-+pb-v v=0=0,则需要在矩阵A A的十字链表中删除papa所指结点,此时需改变该行链表中前趋结点的rightright域,以及该列链表中前趋结点的downdown域。(3 3)若pa-col pa-col colpb
17、-col且pa-colpa-col0 0(即不是表头结点),则只需要将papa指针向右推进一步,并继续进行比较。(4 4) 若pa-col pa-col pb-colpb-col或pa-colpa-col 0 0(即是表头结点),则需要在矩阵A A的十字链表中插入一个pbpb所指结点。由前面建立十字链表算法知,总表头结点的行列域存放的是矩阵的行和列,而各行(列)链表的头结点其行列域值为零,当然各非零元素结点的行列域其值不会为零,下面分析的4 4种情况利用了这些信息来判断是否为表头结点。多维数组和广义表 MLinkAddMat(Ha,Hb)MLinkHa,Hb;Mnode*p,*q,*pa,*p
18、b,*ca,*cb,*qa;if(Ha-row!=Hb-row|Ha-col!=Hb-col)returnNULL;ca=Ha-v_next.next;/ca初始指向A矩阵中第一行表头结点cb=Hb-v_next.next;/cb初始指向B矩阵中第一行表头结点dopa=ca-right;/pa指向A矩阵当前行中第一个结点qa=ca;/qa是pa的前驱pb=cb-right;/pb指向B矩阵当前行中第一个结点while(pb-col!=0)/当前行没有处理完if(pa-colcol&pa-col!=0)qa=pa;pa=pa-right;elseif(pa-colpb-col|pa-col=0)
19、p=newMNode;p-row=pb-row;p-col=pb-col;p-v=pb-v;p-right=pa;qa-right=p;/新结点插入*pa的前面pa=p;/新结点还要插到列链表的合适位置,先找位置,再插入q=Find_JH(Ha,p-col);/从列链表的头结点找起while(q-down-row!=0&q-down-rowrow)q=q-down;p-down=q-down;/插在*q的后面q-down=p;pb=pb-right;else/第一、二种情况x=pa-v_next.v+pb-v_next.v;if(x=0)/第二种情况qa-right=pa-right;/从行链
20、中删除,/要从列链中删除,找*pa的列前驱结点q=Find_JH(Ha,pa-col);/从列链表的头结点找起while(q-down-rowrow)q=q-down; q-down=pa-down;deletepa;pa=qa;else/第一种情况pa-v_next.v=x;qa=pa;pa=pa-right;pb=pb-right;ca=ca-v_next.next;/ca指向A中下一行的表头结点cb=cb-v_next.next;/cb指向B中下一行的表头结点while(ca-row=0)/当还有未处理完的行则继续returnHa; 为了保持算法的层次,在上面的算法中用到了一个函数Fin
21、d_JHFind_JH()(),其功能是:返回十字链表H H中第j j列链表的头结点指针。 多维数组和广义表3 3 矩阵的转置设SPMatrixA; ; 表示一m*nm*n的稀疏矩阵,其转置B B则是一个n*mn*m的稀疏矩阵,因此也有SPMatrixB; ; 由稀疏矩阵A A求它的转置B B只要将A A的行、列转化成B B的列、行。将A.dataA.data中每一三元组的行列交换后转化到B.dataB.data后,似乎已完成了转置,其实不然。A A的转置B B如图6.156.15所示,图6.166.16是它对应的三元组存储。也就是说,在A A的三元组存储基础上得到B B的三元组表存储。多维数
22、组和广义表转置算法的实质是将矩阵A A的行和列转化成矩阵B B的列和行。sparmatrix Trans(sparmatrix A) / sparmatrix Trans(sparmatrix A) / 调用稀疏矩阵A A sparmatrix B; / sparmatrix B; / 定义稀疏矩阵B B B.rows=A.cols; B.rows=A.cols; B.cols=A.rows;B.cols=A.rows;B.terms=A.terms;B.terms=A.terms; for (int n=0;n=A.terms-1;n+) for (int n=0;ntag=0; gh-ta
23、g=0; gh-node.data=*s; gh-node.data=*s; gh-link=NULL; gh-link=NULL; elseelse gh=new linknode; gh=new linknode; gh-tag=1; gh-tag=1; p=gh; p=gh; s+; s+; strncpy(subs,s,len-2); strncpy(subs,s,len-2); subslen-2=0; subslen-2=0; 多维数组和广义表dodo Disastr(subs,hstr); Disastr(subs,hstr); r=CreateGL(hstr); r=Creat
24、eGL(hstr); p-node.sublist=r; p-node.sublist=r; q=p; q=p; len=strlen(subs); len=strlen(subs); if (len0) if (len0) p=new linknode; p=new linknode; p-tag=1; p-tag=1; q-link=p; q-link=p; while (len0); while (len0); q-link=NULL; q-link=NULL; return gh; return gh; 创建广义表算法的时间复杂度为O(n)O(n)。多维数组和广义表2 2广义表取头算法
25、 若 广 义 表 GL=GL=( a1, a2, , an) , 则head(GL)=a1。取表头运算的结果可以是原子,也可以是一个子表。 例如,head(a1,a2),(a3,a4,a5),a6)=(a1,a2)。Head(linknode *GL)Head(linknode *GL) if GL-tag=1 if GL-tag=1 p=GL-hp; p=GL-hp; return p; return p; 多维数组和广义表3. 3. 广义表取尾算法 若广义表GL=GL=(a1,a2,an),则tail(GL)=(a2,an) 。取表尾运算的结果是取出除表头以外的所有元素。 例如,tail(
26、a1,a2),(a3,a4,a5),a6)=(a3,a4,a5),a6)。 Tail(linknode *GL)Tail(linknode *GL) if GL-tag=1 if GL-tag=1 p=GL-tp; p=GL-tp; return p; return p; 多维数组和广义表4. 4. 求广义表深度算法int Depth(linknode *GL)int Depth(linknode *GL) int max=0; int max=0;while (GL!=NULL)while (GL!=NULL) if (GL-tag=0) if (GL-tag=0) / / 有子表 int
27、dep=Depth(GL-sublit) int dep=Depth(GL-sublit) if (depmax) if (depmax) max=dep; max=dep; GL=GL-link;GL=GL-link; return max+1; return max+1; / / 非空表的深度是各元素的深度的最大值加1 1 求广义表深度算法的时间复杂度为O(n)O(n)。多维数组和广义表(1)数组是一个具有固定格式和数量的数据有序集,每一个数据元素有唯一的一组下标来标识,在数组上不太适宜做插入、删除数据元素的操作。(2 2)二维数组中aij的地址为:LOC(aij)=LOC(a00)+(i
28、n+j)d(0下标起始的语言)(3)三维数组中aijk的地址为:LOC(aijk)=LOC(a000)+(inp+jp+k)d(0下标起始的语言)(4)对称矩阵是一种特殊矩阵,n阶方阵的元素满足性质: aij=aji(0i,jn-1)。对称矩阵是关于主对角线的对称,因此只需存储上三角或下三角部分的数据即可。小 结多维数组和广义表(5)三角矩阵的特殊性是以主对角线划分矩阵。主对角线任意一侧(不包括主对角线中)的元素均为常数(C)。下三角矩阵,主对角线以上均为同一个常数;上三角矩阵,主对角线以下均为同一个常数,均可以采用压缩存储。(6 6)在m*nm*n的矩阵中有t t个非零元素,且t t远小于m
29、*nm*n,这样的矩阵称稀疏矩阵。稀疏矩阵常用的有:三元组表存储、带行指针的链表存储、十字链表存储等存储方法。(7)广义表是n(n0)个数据元素的有序序列,广义表的元素可以是单元素,也可以是一个广义表。(8)由于广义表的元素有两种形式,所以其结点的存储形式也有两种:表结点由标志域、表头指针域、表尾指针域组成;而原子结点由标志域和值域组成。多维数组和广义表1 1实验目的 (1 1)掌握稀疏矩阵三元组表的存储方法。 (2 2)掌握稀疏矩阵三元组表创建、显示、转置和查找等算法。 (3 3)掌握广义表的存储方法。 (4 4)掌握广义表的新建、显示和查找等算法。 (5 5)掌握稀疏矩阵三元组表和广义表的
30、算法分析方法。2 2实验内容 (1 1)编写稀疏矩阵三元组表的存储程序; (2 2)编写疏矩阵三元组表创建、显示、转置和查找程序; (3 3)编写建立广义表的程序; (4 4)编写广义表的显示和查找程序; (5 5)设计选择式菜单,其一级菜单形式如下:验证性实验6 6: 稀疏矩阵和广义表子系统系统多维数组和广义表稀疏矩阵和广义表子系统* * 1-1-稀 疏 矩 阵 * * * 2-2-广 义 表 * * * 0-0-退 出 * * 请输入菜单号(0-20-2): : 稀疏矩阵二级菜单形式如下: 稀疏矩阵的三元组存储* * 1-1-新 建 * * * 2-2-转 置 * * * 3-3-查 找 * * * 4-4-显 示 * * * 0-0-返 回 * * 请输入菜单号(0-40-4): : 多维数组和广义表