1 / 63

数据结构

数据结构. 主讲人:刘亚姝 E_mail: answer_lys@163.com. 第五章 数组和广义表. 数组的定义 数组的顺序表示和实现 矩阵的压缩存储 广义表的定义 广义表的存储结构 M 元多项式的表示. 数组的回顾. 定义 一种 “ 连续的存储区域 ” ,并使用一个 称指向此区域的起点,通过名称及偏移的方 式,可以很容易的取到该区域的数据,此即 为数组。. 在程序设计语言中,数组被定义为连续的、 有限的,有序的同种元素的集合。. 数组的回顾. C 语言中一维数组的定义及使用: 类型说明符 数组名 [ 常量表达式 ] ; 例如:

urbana
Télécharger la présentation

数据结构

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. 数据结构 主讲人:刘亚姝 E_mail: answer_lys@163.com

  2. 第五章 数组和广义表 • 数组的定义 • 数组的顺序表示和实现 • 矩阵的压缩存储 • 广义表的定义 • 广义表的存储结构 • M元多项式的表示

  3. 数组的回顾 定义 一种“连续的存储区域”,并使用一个 称指向此区域的起点,通过名称及偏移的方 式,可以很容易的取到该区域的数据,此即 为数组。 在程序设计语言中,数组被定义为连续的、 有限的,有序的同种元素的集合。

  4. 数组的回顾 C语言中一维数组的定义及使用: 类型说明符数组名[常量表达式]; 例如: int a[10] 操作举例:读入数据 For (I=0;I<=9;I++) scanf(“%d”,&a[I]);

  5. 数组的回顾 C语言中二维数组的定义及使用: 类型说明符数组名[常量表达式][常量表达式]; 例如: int a[3][4] 操作举例:读入数据 For (I=0;I<=2;I++) for (j=0;j<=3;j++) t += a[I][j];

  6. ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( ) 5.1 数组和广义表 数组可以看成是一种特殊的线性表,即线性表中数据 元素本身也是一个线性表 数组的定义和特点 • 定义 • 数组特点 • 数组结构固定 • 数据元素同构 • 数组运算 • 给定一组下标,存取相应的数据元素 • 给定一组下标,修改数据元素的值

  7. 从另一个角度理解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];

  8. 数组的逻辑定义: 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

  9. 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 数组的顺序存储结构 • 次序约定 • 以行序为主序 • 以列序为主序

  10. 对于n维数组,数据元素存储位置的计算公式:对于n维数组,数据元素存储位置的计算公式: LOC(j1,j2,…,jn)=LOC(0,0,…0)+ N维数组以行序为主序的存储结构中数据元素 存储位置的计算公式 Cn=L, Ci-1=bi*Ci

  11. #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,…); 数组的顺序存储表示和实现

  12. 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]; }

  13. 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; }

  14. 数组的偏移量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; }

  15. 数组的取值 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; }

  16. 数组的赋值 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; }

  17. 这个矩阵中有一半的元素是0,如果按行存储将这个矩阵中有一半的元素是0,如果按行存储将 浪费一半存储空间 a11 0 0…….. 0 a11a12…. … …..a1n a21 a220…….. 0 a21a22…….. …….a2n ………………….0 …………………. an1 an2 an3…….. ann an1 an2……..ann 这个矩阵中有一半的元素重复的,将浪费一半 存储空间

  18. 5.3 矩阵的压缩存储 • 定义:对于多个值相同的元只分配一个存储空间, 对零元不分配存储空间 特殊矩阵:假若值相同的元素或者零元素在矩阵 中的分布有一定的规律,则称为特殊矩阵反之称 为稀疏矩阵。

  19. 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 矩阵的压缩存储 • 对称矩阵

  20. 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阶矩阵 • 三角矩阵

  21. 对角矩阵 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

  22. 稀疏矩阵 • 描述:非零元较零元少,且分布没有一定规律的矩阵 • 假设在m*n的矩阵中,有t个元素不为0,则有 δ=t/(m×n),认为δ≤ 0.05时为稀疏矩阵 • 压缩存储原则:只存矩阵的行列维数和每个非零元的行列下标及其值

  23. 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

  24. 压缩存储稀疏矩阵--只存储稀疏矩阵的非零元压缩存储稀疏矩阵--只存储稀疏矩阵的非零元 压缩存储稀疏矩阵,不仅仅要存储非零元的值还要 存储非零元的位置。 用三元组表示: ((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)就可以唯一确定一个稀疏矩阵

  25. 稀疏矩阵的压缩存储方法 • 顺序存储结构 • 三元组顺序表 #define MAXSIZE 12500 Typedef struct{ int I,j; ElemType e; }Triple; Typedef struct{ Triple data[MAXSIZE+1]; int mu,nu,tu; }TSMatrix

  26. 非零元值 行列下标 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

  27. 求转置矩阵 • 问题描述:已知一个稀疏矩阵的三元组表,求该矩阵转置矩阵的三元组表 • 问题分析 一般矩阵转置算法: for(col=0;col<n;col++) for(row=0;row<m;row++) n[col][row]=m[row][col]; 时间复杂度:T(n)=O(mn)

  28. 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

  29. 解决思路:只要做到 将矩阵行、列维数互换 将每个三元组中的i和j相互调换 重排三元组次序,使mb中元素以N的行(M的列)为主序 方法一:按M的列序转置 即按mb中三元组次序依次在ma中找到相应的三元组进行 转置。为找到M中每一列所有非零元素,需对其三元组表 ma从第一行起扫描一遍。由于ma中以M行序为主序,所以 由此得到的恰是mb中应有的顺序

  30. 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

  31. 算法描述: 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)

  32. cpot[1]=1; cpot[col]=cpot[col-1]+num[col-1]; (2col a.nu) 方法二:快速转置 即按ma中三元组次序转置,转置结果放入b中恰当位置 此法关键是要预先确定M中每一列第一个非零元在mb中位 置,为确定这些位置,转置前应先求得M的每一列中非零 元个数 实现:设两个数组 num[col]:表示矩阵M中第col列中非零元个数 cpot[col]:指示M中第col列第一个非零元在mb中位置 显然有:

  33. 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

  34. 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

  35. 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 与mn同数量级,则T(n)=O(mn)

  36. 三元组顺序表(双下标法)特点: 非零元在表中按行有序存储,因此便于依行顺序处理的 矩阵运算。 然而,若需按行号存取某一行的非零元,则需要按行 进行查找。

  37. 稀疏矩阵的压缩存储方法 • 顺序存储结构 • 行逻辑链接的顺序表 将指示“行”信息的数组cpot固定在稀疏矩阵的存储结构中,称这种“带行链接信息”的三元组表为行逻辑链接的顺序表。

  38. 类型描述: #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;

  39. 矩阵相乘 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]; }

  40. 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

  41. 操作步骤: 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中。

  42. 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

  43. 两个稀疏矩阵相乘Q=M*N的过程可大致描述如下:两个稀疏矩阵相乘Q=M*N的过程可大致描述如下: Q非零矩阵; Q初始化; 逐行求积  各行累加器清零      取得相乘的累加和并保存到临时数组ctemp中      将ctemp中非零元存储到Q.data中

  44. 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)

  45. 稀疏矩阵的压缩存储方法 • 链式存储结构 • 十字链表 • 十字链表 • 设行指针数组和列指针数组,分别指向每行、列第一个非零元 • 结点定义

  46. 1 3 2 5 4 8 2 4 2 col val row down right 1 3 1 ^ ^ ^ ^ ^ ^

  47. Typedef struct OLNode{ int I,j; ElemType e; struct OLNode*right,*down; }OLNode; *Olink; Typedef struct{ Olink*rhead,*chead; int mu,nu,tu; }CrossList;

  48. 从键盘接收信息建立十字链表算法 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指针

  49. 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)

More Related