1 / 30

第五章 数组和广义表

第五章 数组和广义表. 线性结构的主要特点:数据元素的有序性。 数据元素可以是原子类型,聚合类型,还可以是数据结构。 数 组:线性表的元素也是线性表。 广义表:表的元素既可以是原子,也可以是表。. 数组的定义. 一、定义 1 : 一个 N 维数组是受 N 组线 性关系约束的线性表。 二维数组的逻辑结构可形式地描述为 : 2 _ARRAY(D,R) 其中 D={a ij } | i=0,1,...,b 1 -1; j=0,1,...,b 2 -1;a ij ∈D 0 }

callum
Download Presentation

第五章 数组和广义表

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第五章 数组和广义表 线性结构的主要特点:数据元素的有序性。 数据元素可以是原子类型,聚合类型,还可以是数据结构。 数 组:线性表的元素也是线性表。 广义表:表的元素既可以是原子,也可以是表。

  2. 数组的定义 一、定义1: 一个 N 维数组是受 N 组线 性关系约束的线性表。 二维数组的逻辑结构可形式地描述为: 2_ARRAY(D,R) 其中 D={aij} | i=0,1,...,b1-1; j=0,1,...,b2-1;aij∈D0} R={Row,Col} Row={<aij,ai,j+1>|0<=i<=b1-1;0<=j<=b2-2;aij,ai,j+1∈D0} ai,j+1是aij在行关系中的后继元素。 Col={<aij,ai+1,j>|0<=i<=b1-2;0<=j<=b2-1;aij,ai+1,j∈D0} ai+1,j是aij在列关系中的后继元素。 ①每一个数组元素a[i,j]都受两个关系Row和Col的约束: ROW(行关系):ai,j+1是aij在行关系中的直接后继。 COL(列关系):ai+1,j是aij在列关系中的后继元素。 ②每个数组元素属于同一数据类型。 ③每个数组元素由下标(i,j)唯一确定其位置。 ④每个下标i由bi限定其范围,0≤i≤bi-1

  3. 数组的定义 n维数组的逻辑结构可描述为: n_ARRAY(D,R) D---数组的元素 R---定义为数组元素间的关系 R=(R1,R2,...,Rn) Rj={<a(i1,i2,…,ij,…in),a(i1,i2,…,ij+1,…in)> |0≤ik≤bk-1,1≤k≤n,k≠j, 0≤ij≤bj-2, a(i1,i2,…,ij,…in),a(i1,i2,…,ij+1,…in)∈D0} n ①数组元素个数:∏bi i=1 ②一个元素a(i1,i2,…,ij,…in)都受n个关系的约束,每个关系都是线性关系。 ③每个数组元素对应于一组下标(j1,j2,...,jn) ④每个下标的取值范围为0≤ji≤bi-1(i=1,2,...,n)。

  4. a00 a01 ... a0n-1 a10 a11 ... a1n-1 …… am-1,0 am-1,1 ... am-1,n-1 a00 a01 ... A0n-1 a10 a11 ... A1n-1 …… Am-1,1 am-1,1 ... Am-1,n-1 数组的定义 二、定义2: 一维数组是定长线性表; 二维数组是一个定长线性表,它的每个元素是一个一维数组; n维数组是线性表,它的每个元素是n-1维数组。 ((a00,a01,...,a0n-1),(a10,a11,...,a1n-1),…,(am-1,0,am-1,1,...,am-1,n-1)) 数组是线性结构,基于两点: 1、一个 n维数组被定义为一个线性表,它的元素是一个 n-1维数组。 2、一个 n维数组的数据元素受n个关系的约束,且每个关系都是线性的。

  5. 数组的基本操作 1、数组初始化:确定数组维数、长度,分配存储空间。 initarray(&A,n,bound[ ]); bound[ ]= b1,b2......bn 2、撤消数组 destroyarray (&A); 3、求数组元素值 value(A,&e,index[ ]); index[ ]= i1,i2,......in 4、为数组元素赋值 assign(&A,e,index[ ]); 常用运算

  6. 数组的顺序表示及实现 一、顺序分配: 用一遍地址连续的存储单元依次存放数据元素。 二、二维数组分配方式(数组定义2): 1、行主分配:将数组元素按行的顺序依次排列。 2、列主分配:将数组元素按列的顺序依次排列 三、存储映像:数组下标与存储位置之间的对应关系。 1、行主分配: LOC(i,j)=LOC(0,0)+(i*b2+j)L 2、列主分配: LOC(i,j)=LOC(0,0)+(i+j*b1)L

  7. n-1 n =loc(0,0,......0)+ (∑ji Ⅱbk+jn)L i=1 k=i+1 n 可缩写为: loc(j1,j2,.....jn)=loc(0,0,......0)+ ∑ciji i=1 其中: cn =L, ci-1= bi × ci, 1<i ≤ n ; ci为常数 上式称为n维数组的存储映象函数 数组的顺序表示及实现 四、n数组的存储映象 loc(j1,j2,.....jn)=loc(0,0,......0)+ (b2×b3×…×bn j1+ b3…×bn×j2+ ...... +bn×jn-1+ jn)L

  8. 数组基本操作的实现 1、数据类型描述 #define MAX_ARRAY_DIM 8 typedef struct { ElemType *base; //数组元素空间 int dim; //数组维数 int *bounds; //数组维长 int *constant; //常数因子 }ARRAY; 数组初始化:确定结构中各字段的值。

  9. 数组初始化操作的实现 Status initarray(Array &A,int n,int bound[ ]); { if(n<1‖n>Max_Array_dim) return ERROR A.dim=n; if(!A.bounds=(int *)malloc(n*sizeof(int))) return ERROR; elemtotal=1; for (i=0;i<n;++i) //计算数组元素个数 { A.bounds[i]=bound[i]; if (bound[i]<0) return ERROR; elemtotal*=bound[i]; } A.base =(Elemtype *)malloc(elemtotal *sizeof(Elemtype)); if (!A.base ) return ERROR; //分配元素空间 A.constants=(int *)malloc(n*sizeof(int)); if (!A.constants) return ERROR; A.constants[n-1]=1; for (i=n-2;i>=0;--i) //计算常数因子 A.constants[i]=A.bounds[i+1]*A.constants[i+1]; return OK; }

  10. 数组撤消操作的实现 释放数组占用的所有存储空间 Status destroyarray(Array &A) { if (!A.base) return ERROR; free(A.base); A.base=NULL; if(!A.bounds) return ERROR; free(A.bounds);A.bounds=NULL; if(!A.constants) return ERROR; free(A.constants);A. constants =NULL; return OK; }

  11. 取数组元素值操作的实现 核心:判断元素下标合法性,计算数组元素地址 Status value(Array A,elemtype &e,int index[ ]); //index中存放数组元素下标 { off=0; for(i=0;i<A.dim;++i) { if(index[i]<0||index[i]>=A.bounds[i]) return ERROR; off+=A.constants[i]*index[i]; } e=*(A.base+off); return OK; }

  12. 为数组元素赋值操作的实现 核心:判断元素下标合法性,计算数组元素地址 Status Assign(Array A,elemtype e,int index[ ]); { off=0; for(i=0;i<A.dim;++i) { if(index[i]<0||index[i]>=A.bounds[i]) return Error; off+=A.constants[i]*index[i]; } *(A.base+off)=e; return OK; }

  13. 矩阵的压缩存储 一、矩阵压缩存储的概念 ㈠特殊矩阵:值相同的元素或0元素在矩阵中的分布有一定规律。 ⒈对称矩阵:矩阵中的元素满足 aij=aji 1≤i,j≤n ⒉三角矩阵:上(下)三角矩阵指矩阵的下(上)三角(不包括对角线)中的元素均为常数c或0的n阶矩阵。 ⒊对角矩阵(带状矩阵):矩阵中所有非0元素集中在以主对角线为中心的区域中。 ㈡稀疏矩阵:非0元素很少( ≤5%)且分布无规律。 二、矩阵的压缩存储 为多个相同值的元分配一个存储单元;对零元不分配空间。

  14. i(i-1)/2+j-1 i>=j j(j-1)/2+i-1 i< j K= k=0,1,…,n(n+1)/2-1 对称矩阵的压缩存储 存储分配策略: 每一对对称元只分配一个存储单元,即只存储下三角(包括对角线)的元, 所需空间数为: n×(n+1)/2。 存储分配方法: 用一维数组sa[n(n+1)/2]作为存储结构。 sa[k]与aij之间的对应关系为: 0 1 2 3 n(n-1)/2 n(n+1)/2-1

  15. 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 稀疏矩阵 一、存储分配策略 只存储稀疏矩阵的非0元素。 一个三元组(i,j,aij)唯一确定了一个非0元素。 逻辑上,用三元组表来表示稀疏矩阵的非0元 三元组表----以三元组为元素的线性表: ((1,2,12),(1,3,9),(3,1,-3),(3,6,14),(4,3,24),(5,2,18),(6,1,15),(6,4,-7))

  16. typedef struct { int i , j; //行号,列号 EelemType e; //非0元值 } triple 三元组 typedef struct { triple *data //三元组表 int mu , nu , tu; //行、列、非0元个数 } TSMatrix 三元组 顺序表 稀疏矩阵 二、三元组表的顺序实现 用顺序存储结构来表示三元组表----三元组顺序表。

  17. i j v i j v [1 2 12] [1 3 9] [3 1 –3] [3 6 14] [4 3 24] [5 2 18] [6 1 15] [6 4 –7] 6 7 8 [1 3 –3] [1 6 15] [2 1 12] [2 5 18] [3 1 9] [3 4 24] [4 6 –7] [6 3 14] 7 6 8 b.data a.data 稀疏矩阵 二、三元表顺序表的转置运算 矩阵的转置:矩阵行列互换。 M: m×n矩阵 转置=>T:n×m矩阵 T[i][j] = M[j][i] 1≤i≤n, 1≤j≤m

  18. ije ije [ 1 3 –3] [ 1 6 15] [ 2 1 12] [ 2 5 18] [ 3 1 9] [ 3 4 24] [ 4 6 –7] [ 6 3 14] ③ [ 1 2 12] [ 1 3 9] [ 3 1 –3] [ 3 6 14] [ 4 3 24] [ 5 2 18] [ 6 1 15] [6 4 –7] ① ⑤ ② ④ ⑧ ⑥ ⑦ mu nu tu 7 6 8 mu nu tu 6 7 8 稀疏矩阵转置 三、三元表顺序表求矩阵转置的方法 1、将矩阵的行、列值互换:b.mu=a.nu;b.nu=a.mu; 2、将每个三元组的行、列值互换 3、重排三元组的次序 方法1:按照b矩阵中的行次序依次在a.data中找到相应的三元组进行转置。

  19. 稀疏矩阵 Status TransposeSmatrix (TSmatrix M, TSmatrix &T) { T.mu=M.nu; T.nu=M.mu; T.tu=M.tu; if(tu){ pt=0; for (col=1;col<=M.nu;++col) for(pm=1;pm<=M.tu;++pm) if(M.data[pm].j==col) { T.data[pt].i=M.data[pm].j; T.data[pt].j=M.data[pm].i; T.data[pt].e=M.data[pm].e; pt++; } } return OK; } 算法时间复杂度为O(nu*tu)。 当tu与m*n同数量级时,算法时间复杂度为O(m*n2)。

  20. cpot[1]=0; col=1 cpot[col]=cpot[col-1]+num[col-1] 2≤col≤a.nu 稀疏矩阵 方法2:按照a.data中三元组的次序进行转置,并将转置后的三元组放到b.data中的恰当位置。 恰当位置的确定:首先计算M矩阵的每一列(即T的每一行)中非0元的个数,然后求得M矩阵每一列第一个非0元在b.data中的位置。 算法基本思想: 设置两个向量: num[col]:第col列的非零元素个数。 cpot[col]:第col列第一个非零元在b.data中的恰当位置。 在转置过程中,指示该列下一个非零元在b.data中的位置。 1、num[col]的计算: 顺序扫描a.data三元组,累计各列非0元个数。 2、cpot[col]计算:

  21. 稀疏矩阵 void fastransposeSMatrix(TSMatrix M,TSMatrix &T) { T.mu=M.nu; T.nu=M.mu; T.tu=M.tu; if (T.tu<>0) { for(col=1; col<=M.nu; col++) num[col]=0; /* 初始化*/ for(t=0;t<M.tu;t++) num[M.data[t].j]= num[M.data[t].j]+1; /*求M中每一列非零元个数*/ cpot[1]=0; /*求第col列中第一个非零元在b.data中的序号*/ for(col=2;col<=M.nu;col++) cpot[col]=cpot[col-1]+num[col-1]; for(p=0;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]; } } } 算法时间复杂度为O(nu+tu)。 当tu与mu*nu同数量级时,算法时间复杂度为O(mu*nu)。

  22. (ije) (ije) (152) (223) (344) (475) (127) (154) (245) (344) (439) (472) mu nu tu 4 7 4 mu nu tu 4 7 6 稀疏矩阵相加 条件:两相加矩阵行、列相同。 方法:与两多项式相加相同,比较当前两处理元素: 若行列号相同:元素相加,结果不为0时写入结果稀疏矩阵。 列号不同:把列号小的写入结果稀疏矩阵。 行号不同:把行号小的写入结果稀疏矩阵。 (ije) (127) (156) (223) (245) (348) (439) (477) + = mu nu tu 4 7 7

  23. 稀疏矩阵相加 void AddSmatrix (TSmatrix M1, TSmatrix M2, TSmatrix &M) { M.mu=M1.mu; M.nu=M1.nu; p1=0;p2=0;p=0; while (p1<M1.tu &&p2<M2.tu) { if (M1. data[p1].i== M2. data[p2].i&& M1. data[p1].j== M2. data[p2].j) { M.data[p] =M1.data[p1++]; M.data[p].e+=M2.data[p2++].e; if(M.data[p].e!=0) p++;} else if (M1. data[p1].i< M2. data[p2].i|| M1. data[p1].i== M2. data[p2].i && M1. data[p1].j< M2. data[p2].j) { M.data[p++]=M1.data[p1++]; } else { M.data[p++]=M1.data[p2++]; } } while (p1<M1.tu ){ M.data[p++]=M1.data[p1++];} while (p2<M2.tu ){ M.data[p++]=M2.data[p2++];} M.tu=p; } 算法时间复杂度:O(M1.tu+M2.tu)

  24. i j e 向右域 down right 向下域 稀疏矩阵的十字链表表示 方法:每个非零元用一个结点表示;每行和每列的非零元连成链表。 特点:一个结点既是行链表的结点,也是列链表的结点。 在图形表示中,一个结点处在链表连线的十字中心,故称为十字链表 结点结构: i,j分别表示该数组某非零元素的行、列值。 e表示该非零元素的值。 down指向该列的下一个非零元素结点。 right指向该行的下一个非零元素结点。 行指针数组:存放行链表头指针。 列指针数组:存放列链表头指针。

  25. 2 1 3 1 2 1 2 -1 3 M.chead 例 M.rhead 1 4 5 ^ ^ ^ ^ ^ ^ 稀疏矩阵的十字链表表示 typedef struct OLNode{ int i, j; ElemType e; struct OLNode *right, *down; } OLNode, *Olink; typedef struct{ Olink *rhead, *chead; int mu,nu,tu; }CrossList;

  26. 广义表的定义 广义表又称为列表(lists),是n≥0个元素a1,a2,...,an的有限序列,记为: A=( a1,a2,...,an) 其中: A是广义表的表名,n是广义表的长度 ai是单个元素或广义表, 若ai是单个元素,则称为广义表的单元素(或原子)。 若是广义表,则称ai是广义表的子表。所以广义表又称为列表。 即 ai ∈D0或 ai ∈lists 广义表的表头(Head):非空表A 的第一个元素 a1。 广义表的头与a1具有相同的表示形式。 广义表的表尾(Tail):除其头之外的其余元素( a2,...,an)组成的表。 广义表的尾一定是一个广义表。 特点:广义表的定义是一个递归的定义。

  27. 广义表的定义 二、广义表示例: A=( ) ; 空表 B=(e) ; n=1, 表头=e, 表尾=( ) C=(a,(b,c,d)) ; n=2, 表头=a, 表尾=((b,c,d) ) D=(A,B,C) ; n=3, 表头=A, 表尾=(B,C ) E=(a,E) ; n=2, 表头=a, 表尾=(E ),E 称为递归表 三、结论 1、广义表描述的是一个层次结构。 2、广义表可以为其它广义表所共享。 3、广义表可以是一个递归表,表名本身是表的一个元素。

  28. tag=0 atom 原子结点 表结点 tag=1 hp tp 表头指针 表尾指针 广义表的存储结构 广义表链式存储表示: ai 有两种类型:原子(单元素)、表。 表可以分成表头和表尾。 typedef enun{ATOM,LIST}Elemtag; typedef struct GLnode { Elemtag tag; union { Atomtype atom; struct {GLnode *hp,*tp;}ptr; } }*Glists

  29. 0 0 0 e a e 0 0 0 b d c 1 1 1 1 1 1 1 1 1 1 ^ ^ ^ ^ ^ B C 1 ^ D E 广义表的存储结构 A=Null A=( ) B=(e) C=(a,(b,c,d)) D=(A,B,C) E=(a,E)

  30. 作业 5.1 5.2 5.3 5.5 5.6 5.7 5.10 5.11 *5.18 5.19

More Related