300 likes | 423 Views
第五章 数组和广义表. 线性结构的主要特点:数据元素的有序性。 数据元素可以是原子类型,聚合类型,还可以是数据结构。 数 组:线性表的元素也是线性表。 广义表:表的元素既可以是原子,也可以是表。. 数组的定义. 一、定义 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 }
E N D
第五章 数组和广义表 线性结构的主要特点:数据元素的有序性。 数据元素可以是原子类型,聚合类型,还可以是数据结构。 数 组:线性表的元素也是线性表。 广义表:表的元素既可以是原子,也可以是表。
数组的定义 一、定义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
数组的定义 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)。
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个关系的约束,且每个关系都是线性的。
数组的基本操作 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[ ]); 常用运算
数组的顺序表示及实现 一、顺序分配: 用一遍地址连续的存储单元依次存放数据元素。 二、二维数组分配方式(数组定义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
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
数组基本操作的实现 1、数据类型描述 #define MAX_ARRAY_DIM 8 typedef struct { ElemType *base; //数组元素空间 int dim; //数组维数 int *bounds; //数组维长 int *constant; //常数因子 }ARRAY; 数组初始化:确定结构中各字段的值。
数组初始化操作的实现 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; }
数组撤消操作的实现 释放数组占用的所有存储空间 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; }
取数组元素值操作的实现 核心:判断元素下标合法性,计算数组元素地址 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; }
为数组元素赋值操作的实现 核心:判断元素下标合法性,计算数组元素地址 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; }
矩阵的压缩存储 一、矩阵压缩存储的概念 ㈠特殊矩阵:值相同的元素或0元素在矩阵中的分布有一定规律。 ⒈对称矩阵:矩阵中的元素满足 aij=aji 1≤i,j≤n ⒉三角矩阵:上(下)三角矩阵指矩阵的下(上)三角(不包括对角线)中的元素均为常数c或0的n阶矩阵。 ⒊对角矩阵(带状矩阵):矩阵中所有非0元素集中在以主对角线为中心的区域中。 ㈡稀疏矩阵:非0元素很少( ≤5%)且分布无规律。 二、矩阵的压缩存储 为多个相同值的元分配一个存储单元;对零元不分配空间。
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
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))
typedef struct { int i , j; //行号,列号 EelemType e; //非0元值 } triple 三元组 typedef struct { triple *data //三元组表 int mu , nu , tu; //行、列、非0元个数 } TSMatrix 三元组 顺序表 稀疏矩阵 二、三元组表的顺序实现 用顺序存储结构来表示三元组表----三元组顺序表。
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
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中找到相应的三元组进行转置。
稀疏矩阵 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)。
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]计算:
稀疏矩阵 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)。
(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
稀疏矩阵相加 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)
i j e 向右域 down right 向下域 稀疏矩阵的十字链表表示 方法:每个非零元用一个结点表示;每行和每列的非零元连成链表。 特点:一个结点既是行链表的结点,也是列链表的结点。 在图形表示中,一个结点处在链表连线的十字中心,故称为十字链表 结点结构: i,j分别表示该数组某非零元素的行、列值。 e表示该非零元素的值。 down指向该列的下一个非零元素结点。 right指向该行的下一个非零元素结点。 行指针数组:存放行链表头指针。 列指针数组:存放列链表头指针。
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;
广义表的定义 广义表又称为列表(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)组成的表。 广义表的尾一定是一个广义表。 特点:广义表的定义是一个递归的定义。
广义表的定义 二、广义表示例: 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、广义表可以是一个递归表,表名本身是表的一个元素。
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
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)
作业 5.1 5.2 5.3 5.5 5.6 5.7 5.10 5.11 *5.18 5.19