630 likes | 781 Views
数据结构. 主讲人:刘亚姝 E_mail: answer_lys@163.com. 第五章 数组和广义表. 数组的定义 数组的顺序表示和实现 矩阵的压缩存储 广义表的定义 广义表的存储结构 M 元多项式的表示. 数组的回顾. 定义 一种 “ 连续的存储区域 ” ,并使用一个 称指向此区域的起点,通过名称及偏移的方 式,可以很容易的取到该区域的数据,此即 为数组。. 在程序设计语言中,数组被定义为连续的、 有限的,有序的同种元素的集合。. 数组的回顾. C 语言中一维数组的定义及使用: 类型说明符 数组名 [ 常量表达式 ] ; 例如:
E N D
数据结构 主讲人:刘亚姝 E_mail: answer_lys@163.com
第五章 数组和广义表 • 数组的定义 • 数组的顺序表示和实现 • 矩阵的压缩存储 • 广义表的定义 • 广义表的存储结构 • M元多项式的表示
数组的回顾 定义 一种“连续的存储区域”,并使用一个 称指向此区域的起点,通过名称及偏移的方 式,可以很容易的取到该区域的数据,此即 为数组。 在程序设计语言中,数组被定义为连续的、 有限的,有序的同种元素的集合。
数组的回顾 C语言中一维数组的定义及使用: 类型说明符数组名[常量表达式]; 例如: int a[10] 操作举例:读入数据 For (I=0;I<=9;I++) scanf(“%d”,&a[I]);
数组的回顾 C语言中二维数组的定义及使用: 类型说明符数组名[常量表达式][常量表达式]; 例如: int a[3][4] 操作举例:读入数据 For (I=0;I<=2;I++) for (j=0;j<=3;j++) t += a[I][j];
( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) 5.1 数组和广义表 数组可以看成是一种特殊的线性表,即线性表中数据 元素本身也是一个线性表 数组的定义和特点 • 定义 • 数组特点 • 数组结构固定 • 数据元素同构 • 数组运算 • 给定一组下标,存取相应的数据元素 • 给定一组下标,修改数据元素的值
从另一个角度理解n维数组,以二维数组为例:从另一个角度理解n维数组,以二维数组为例: A=(α0α1…αi…αp)(p=m-1,p=n-1) 其中每个数据元素αi是个行向量形式的线性表 αi= (αi0αi1…αii…αin) • 在C语言中,一个二维数组可以定义为其分量类型为一维数组类型的一维数组 Typedef Elemtype Array2[m][n]; 等价于 Typedef Elemtype Array1[n]; Typedef Array1 Array2[m];
数组的逻辑定义: ADT Array{ 数据对象:ji=0,…,bi-1,I=1,2,…,n, D={aj1j2…jn| n(>0)称为数组的维数 bi是第I维的长度 ji是数组元素的第I维的下标, aj1j2…jn∈ ElemSet} 数据关系:R={R1,R2,…,Rn} Ri={< aj1…ji…jn , aj1…ji+1…jn >} 基本操作: InitArray(&A,n,bound1,…,boundn) DestroyArray(&A); Value(&A,e,index1,…,indexn); Assign(&A,e,index1,…,indexn); }ADT Array
a00 0 0 1 a01 按行序为主序存放 a00 按列序为主序存放 1 ……. a10 a0n-1 n-1 ……. a10 m-1 n am-10 m a11 a00 a01 …….. a0n-1 a00 a01……..a0n-1 a01 …….. a10 a11 …….. a1n-1 a10a11 ……..a1n-1 a11 a1n-1 …….. …………………. …………………. ………. am-11 am-10 am-10 am-21 …….. am-1n-1 am-10am-11…….. am-1n-1 ………. am-11 a0n-1 Loc( aij)=Loc(a00)+[n*i+j]*l …….. Loc(aij)=Loc(a00)+[m*j+i]*l a0n-1 (m-1)*(n-1) am-1n-1 …….. (m-1)*(n-1) am-1n-1 5.2 数组的顺序存储结构 • 次序约定 • 以行序为主序 • 以列序为主序
对于n维数组,数据元素存储位置的计算公式:对于n维数组,数据元素存储位置的计算公式: LOC(j1,j2,…,jn)=LOC(0,0,…0)+ N维数组以行序为主序的存储结构中数据元素 存储位置的计算公式 Cn=L, Ci-1=bi*Ci
#include <stdarg.h> #define MAX-ARRAY_DIM 8 Typedef struct{ Elemtype *base; int dim;//数组维数 int *bounds;//各维的维界基址 int *constants;//偏移地址ci×bi }Array; Status InitArray(Array &A,int dim,…); Status DestroyArray(Array &A); Status Value(Array A,ElemType &e,…); Status Assign(Array &A,ElemType e,…); 数组的顺序存储表示和实现
Status InitArray(Array &A,int dim,…){ if (dim<1 || dim>MAX_ARRAY_DIM) return Error; A.dim=dim; A.bounds=(int*)malloc(dim*sizeof(int)); if (! A.bounds) exit(OVERFLOW); elemtotal=1; va_start(ap,dim); for(I=0;I<dim;+ +I){ A.bounds[I]=va_arg(ap,int); if (A.bounds[I]<0 ) return UNDERFLOW; elemtotal* = A.bounds[I]; }
va_end(ap); A.base=(ElemType*)malloc(elemtotal* sizeof(ElemType); if (!A.base) exit (OVERFLOW); A.constants=(int*)malloc(dim*sizeof(int)); if (!A.constants) exit(overflow); A.constants[dim-1]=1; for(I=dim-2;I>=0;- -i) A.constants[I]=A.bounds[I+1]*A.constants[I+1]; return OK; }
数组的偏移量Locate Status Locate(Array A,va_list ap,int &off){ off=0; for(I=0;I<A.dim;++I){ ind=va_arg(ap,int); if(ind<0 || ind>=A.bounds[I]) return OVERFLOW; off+=A.constants[I]*ind; } return OK; }
数组的取值 Value Status Value(Array A,Elemtype&e,…){ va_start(ap,e); if ( (result=Locate(A,ap,off))<=0) return result; e=*(A.base+off); return OK; }
数组的赋值 Assign Status Assign(Array A,Elemtypee,…){ va_start(ap,e); if ( (result=Locate(A,ap,off))<=0) return result; *(A.base+off)=e; return OK; }
这个矩阵中有一半的元素是0,如果按行存储将这个矩阵中有一半的元素是0,如果按行存储将 浪费一半存储空间 a11 0 0…….. 0 a11a12…. … …..a1n a21 a220…….. 0 a21a22…….. …….a2n ………………….0 …………………. an1 an2 an3…….. ann an1 an2……..ann 这个矩阵中有一半的元素重复的,将浪费一半 存储空间
5.3 矩阵的压缩存储 • 定义:对于多个值相同的元只分配一个存储空间, 对零元不分配存储空间 特殊矩阵:假若值相同的元素或者零元素在矩阵 中的分布有一定的规律,则称为特殊矩阵反之称 为稀疏矩阵。
12…. … …..7 2 4…….. …….9 …………………. 7 9……..10 按行序为主序: …... …... a11 a21 a22 a31 a32 an1 ann k=0 1 2 3 4 n(n-1)/2 n(n+1)/2-1 j(j-1) i(i-1) Loc(aij)=Loc(a11)+[ +(i-1)]*L Loc(aij)=Loc(a11)+[ +(j-1)]*L 2 2 若 n阶矩阵A中的元满足aij= aji 5.3 矩阵的压缩存储 • 对称矩阵
a11 0 0…….. 0 a21 a220…….. 0 ………………….0 an1 an2 an3…….. ann 按行序为主序: …... …... a11 a21 a22 a31 a32 an1 ann k=0 1 2 3 4 n(n-1)/2 n(n+1)/2-1 i(i-1) Loc(aij)=Loc(a11)+[ +(j-1)]*L 2 矩阵的上(下)三角(不包括对角线)中的元均 为常数c或为0的n阶矩阵 • 三角矩阵
对角矩阵 a11a120 …………… . 0 a21a22a230…………… 0 0a32a33a34 0……… 0 …………………………… 00… an-1,n-2an-1,n-1an-1,n 00… …an,n-1ann. 按行序为主序: …... a11 a12 a21 a22 a23 ann-1 ann …... k=0 1 2 3 4 n(n-1)/2 n(n+1)/2-1 Loc(aij)=Loc(a11)+[2(i-1)+(j-1)]*L
稀疏矩阵 • 描述:非零元较零元少,且分布没有一定规律的矩阵 • 假设在m*n的矩阵中,有t个元素不为0,则有 δ=t/(m×n),认为δ≤ 0.05时为稀疏矩阵 • 压缩存储原则:只存矩阵的行列维数和每个非零元的行列下标及其值
ADT SparseMatrix { 数据对象:D={aij|I=1,2,…,m; j=1,2,…,n; aij∈ ElemSet ,m 和n 分别称为矩阵的行数和列数 数据关系:R={Row,Col} Row={<ai,j,ai,j+1| 1≤ i ≤ m,1≤ j≤ n} Col={<ai,j,ai+1,j|1≤ i≤ m-1,1≤ j≤ n} 基本操作: CreateSMatrix(&M); DestroySMatrix(&M); PrintSMatrix(M); CopySMatrix(M,&T); AddSmatrix(M,N,&Q); SubtMatrix(M,N,&Q); MultSMatrix(M,N,&Q); TransposeSMatrix(M,&T); }ADT • 稀疏矩阵的抽象数据类型定义 稀疏矩阵M与N的行数与列数对应相等, 求Q=M+N,Q=M-N,Q=M*N
压缩存储稀疏矩阵--只存储稀疏矩阵的非零元压缩存储稀疏矩阵--只存储稀疏矩阵的非零元 压缩存储稀疏矩阵,不仅仅要存储非零元的值还要 存储非零元的位置。 用三元组表示: ((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)就可以唯一确定一个稀疏矩阵
稀疏矩阵的压缩存储方法 • 顺序存储结构 • 三元组顺序表 #define MAXSIZE 12500 Typedef struct{ int I,j; ElemType e; }Triple; Typedef struct{ Triple data[MAXSIZE+1]; int mu,nu,tu; }TSMatrix
非零元值 行列下标 i j e 0 1 2 3 4 5 6 7 8 ma TSMatrix Ma; Ma.mu=6;Ma.nu=7;Ma.tu=8 1 2 12 1 3 9 Ma[1].i,Ma[1].j,ma[1].e分别存放 非零元行列数和非零元值 3 1 -3 3 6 14 4 3 24 三元组表所需存储单元个数为3(tu+1) 其中tu为非零元个数 5 2 18 6 1 15 6 4 -7
求转置矩阵 • 问题描述:已知一个稀疏矩阵的三元组表,求该矩阵转置矩阵的三元组表 • 问题分析 一般矩阵转置算法: for(col=0;col<n;col++) for(row=0;row<m;row++) n[col][row]=m[row][col]; 时间复杂度:T(n)=O(mn)
i j e i j e 1 3 -3 1 2 12 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 1 6 15 1 3 9 mb 2 1 12 3 1 -3 ma 2 5 18 3 6 14 ? 3 1 9 4 3 24 3 4 24 5 2 18 6 1 15 4 6 -7 6 3 14 6 4 -7
解决思路:只要做到 将矩阵行、列维数互换 将每个三元组中的i和j相互调换 重排三元组次序,使mb中元素以N的行(M的列)为主序 方法一:按M的列序转置 即按mb中三元组次序依次在ma中找到相应的三元组进行 转置。为找到M中每一列所有非零元素,需对其三元组表 ma从第一行起扫描一遍。由于ma中以M行序为主序,所以 由此得到的恰是mb中应有的顺序
i j v i j v 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 p p p p p p p p k p p p k p k p p p k p k 1 2 12 1 3 9 3 1 -3 3 6 14 ma mb 4 3 24 5 2 18 6 1 15 6 4 -7 1 3 -3 1 6 15 2 1 12 2 5 18 3 1 9 3 4 24 4 6 -7 6 3 14 col=1 col=2
算法描述: Status TransposeSmatrix(TSMatrix M,TSMatrix &T){ T.mu=M.nu; T.nu=M.mu; T.tu=M.tu; if (T.tu){ q=1; for(col=1;col<=M.nu;++col) for(p=1;p<=M.tu; ++p) if(M.data[p].j==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; ++q } return OK; }//TransposeSMatrix 时间复杂度:O(mu tu)若tu和mu nu 同数量级 则:时间复杂度为O(mu nu2)
cpot[1]=1; cpot[col]=cpot[col-1]+num[col-1]; (2col a.nu) 方法二:快速转置 即按ma中三元组次序转置,转置结果放入b中恰当位置 此法关键是要预先确定M中每一列第一个非零元在mb中位 置,为确定这些位置,转置前应先求得M的每一列中非零 元个数 实现:设两个数组 num[col]:表示矩阵M中第col列中非零元个数 cpot[col]:指示M中第col列第一个非零元在mb中位置 显然有:
col 3 6 7 1 2 4 5 2 num[col] 1 0 2 2 1 0 cpot[col] 1 3 7 8 5 8 9
i j v i j v 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 p p p p p p p p 1 2 12 1 3 9 3 1 -3 col 7 6 1 3 2 5 4 3 6 14 num[col] ma mb 0 2 2 0 1 2 1 4 3 24 9 cpot[col] 8 3 7 8 1 5 5 2 18 6 1 15 6 4 -7 6 4 9 2 5 7 3 1 3 -3 1 6 15 2 1 12 2 5 18 3 1 9 3 4 24 4 6 -7 6 3 14
Status fastTransposeSMatrix(TSMatrix M,TSMatrix&T){ T.mu=M.nu; T.nu=M.mu; T.tu=M.tu; if (T.tu){ for(col=1;col<=M.nu; ++col) num[col]=0; for(t=1;t<=M.tu;++t) ++num[M.data[t].j]; cpot[l]=1; for(col=2;col<M.nu;++col) cpot[col]=cpot[col-1]+num[col-1]; for(p=1;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]; }//for }//if return OK; }// fastTransposeSMatrix • 时间复杂度:T(n)=O(nu+tu) • 若 t 与mn同数量级,则T(n)=O(mn)
三元组顺序表(双下标法)特点: 非零元在表中按行有序存储,因此便于依行顺序处理的 矩阵运算。 然而,若需按行号存取某一行的非零元,则需要按行 进行查找。
稀疏矩阵的压缩存储方法 • 顺序存储结构 • 行逻辑链接的顺序表 将指示“行”信息的数组cpot固定在稀疏矩阵的存储结构中,称这种“带行链接信息”的三元组表为行逻辑链接的顺序表。
类型描述: #define MAXSIZE 12500 Typedef struct{ int I,j; ElemType e; }Triple; Typedef struct{ Triple data[MAXSIZE+1]; int rops[MAXRC+1]; int mu,nu,tu; }RLSMatrix;
矩阵相乘 Q=M*N M是m1*n1矩阵;N是m2*n2矩阵;当n1=m2时: For (I=1;I<m1;++i) For(j=1;j<=n2;++j){ Q[I][j]=0; for(k=1;k<=n1;++k) Q[I][j]+=M[I][k]*N[k][j]; }
M= N= i j e i j e 1 2 2 Q= 2 1 1 3 1 -2 3 2 4 M.data N.data 1 1 3 1 4 5 2 2 -1 3 1 2
操作步骤: 1、在M.data和N.data中找到相应的各对元素即M.data中的j值和N.data中的I值相等的各对元素相乘 即:对于M.data[1..M.tu]中的每个元素(I,k,M(I,k)),在N.data中所有相应的(k,j,N(k,j))元素 2、稀疏矩阵相乘的基本操作:对于M.data[p]找到 满足条件:M.data[p].j=N.data[q].I的元素N.data[q]做乘积,将所有满足条件的乘积累加则为:Q[I][j]的值。 3、两个稀疏矩阵相乘不一定是稀疏矩阵,所以对于所求得累加和,最后进行压缩存储,保存到Q.data中。
row num[row] N= rpos[row] 1 2 3 4 1 0 2 1 5 3 2 1 rpos[row]指示矩阵N的第row行中第一个非零元素 在N.data中的序号, 则rpos[row+1]-1表示的是第row行中最后一个非零元 在N.data中的序号。 最后一个非零元在N.data中的序号为N.tu
两个稀疏矩阵相乘Q=M*N的过程可大致描述如下:两个稀疏矩阵相乘Q=M*N的过程可大致描述如下: Q非零矩阵; Q初始化; 逐行求积 各行累加器清零 取得相乘的累加和并保存到临时数组ctemp中 将ctemp中非零元存储到Q.data中
Status MultSMatrix(RLSMatrix M,RLSMatrix N, RLSMatrix &Q){ if (M.nu!=N.mu) return Error; Q.mu=M.mu;Q.nu=N.nu;Q.tu=0; } If(M.tu*N.tu!=0){ for (arow=1;arow<=M.mu;++arow){ ctemp[]=0; Q.rpos[arow]=Q.tu+1; if (arow<M.mu) tp=M.rops[arow+1]; else(tp=M.tu+1); for(p=M.rpos[arow];p<tp;++p){ brow=M.data[p].j if (brow<N.mu) t=N.rops[brow+1]; else t=N.tu+1; for(q=N.rops[brow];q<t;++q){ ccol=N.data[q].j; ctemp[ccol]+=M.data[p].e*N.data[q].e; }} } } For ((ccol=1;ccol<Q.nu;++ccol) if (ctemp[ccol]){ if (++Q.tu>MAXSIZE) return Error; Q.data[Q.tu]=(arow,ccol,ctemp[ccol]); } 时间复杂度:累加器初始化O(M.mu*N.nu) 求Q所有非零元O(M.tu*N.tu/N.mu) 压缩存储O(M.mu*N.nu) 则,总的时间复杂度O(M.mu*N.nu+M.tu*N.tu/N.mu)
稀疏矩阵的压缩存储方法 • 链式存储结构 • 十字链表 • 十字链表 • 设行指针数组和列指针数组,分别指向每行、列第一个非零元 • 结点定义
1 3 2 5 4 8 2 4 2 col val row down right 1 3 1 ^ ^ ^ ^ ^ ^
Typedef struct OLNode{ int I,j; ElemType e; struct OLNode*right,*down; }OLNode; *Olink; Typedef struct{ Olink*rhead,*chead; int mu,nu,tu; }CrossList;
从键盘接收信息建立十字链表算法 1、初始化rhead与chead指针 2、接收信息生成节点 3、将新生成的节点插入到十字链表中 若:M.chead[I]=Null或者M.rhead[i]->i>i M.chead[I]->i < i M.rhead[I]=Null或者M.rhead[i]->j>j M.rhead[I]->j < j 修改同一行的前一个元素的right指针,修改 同一列元素的down指针
Status CreateSMatrix_OL(CrossList &M){ if(M) free(M); scanf(&m,&n,&t); M.mu=m; M.nu=n; M.tu=t; M.rhead=(Olink*)malloc((m+1)*sizeof(Olink)); if (!M.rhead) then exit(OVERFLOW) M. chead=(Olink*)malloc((n+1)*sizeof(Olink)); if (!M.chead) then exit(OVERFLOW) M.rhead[]=M.chead[]=Null; } for (scanf(&I,&j,&e);I!=0; scanf(&I,&j,&e)){ p= (Olink*)malloc(sizeof(Olink)); if (!p) exit(OVERFLOW); p->I=I; p->j=j; p->e=e; if(M.rhead[I]==NULL|| M.rhead[I]->j>j) {p->right=M.rhead[I];M.rhead[I]=p} else{ for(q=M.rhead[I];(q->right)&&q->right->j<j;q=q->right) {p->right=q->right; q->right=p;} if(M.chead[j]==NULL||M.chead[j]->I>I) {p->down=M.chead[j];M.chead[j]=p;} else{ for(q=M.rhead[j];(q->down)&&q->down->i<i;q=q->down) {p->down=q->down; q->down=p;} } }} return OK 时间复杂度:O(t*s),s=max(m,n)