1 / 32

数组 数据元素可为结构类型的线性表

数组 数据元素可为结构类型的线性表. 数组. 几乎所有的高级程序设计语言中都将数组类型作为固有类型,在此我们以抽象数据类型的形式来讨论其定义和实现. ADT Array{ 数据对象: j i = 0,…,b i -1, i=1,2,…,n D = {a j 1 j 2 …j n | n 称为数组的维数 , b i 是数组第 i 维的长度 , j i 是数组元素的第 i 维下标, a j 1 j 2 …j n ElemSet

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. 数组数据元素可为结构类型的线性表

  2. 数组 • 几乎所有的高级程序设计语言中都将数组类型作为固有类型,在此我们以抽象数据类型的形式来讨论其定义和实现

  3. ADT Array{ 数据对象: ji = 0,…,bi-1, i=1,2,…,n D = {aj1j2…jn | n称为数组的维数, bi是数组第i维的长度, ji是数组元素的第i维下标, aj1j2…jn ElemSet R = {R1, R2, …, Rn} //每个元素受到n个关系的约束 Ri = {<aj1…ji…jn, aj1,,,ji+1…jn> | 0 jk bk-1, 1 k n 且 k i 0 ji bi-2, aj1…ji…jn, aj1,,,ji+1…jn D, i = 2,…n} P: InitArray(&A, n, bound1, …, boundn) DestoryArray(&A) Value(A, &e, index1, …, indexn) //取出元素值 Assign(&A, e, index1, …, indexn) //给元素赋值 }ADT Array 数组的ADT定义

  4. 数组的ADT定义 • n维数组中含有 个数据元素,每个元素受到n个关系的约束,且这n个关系都是线性关系。当n=1时,n维数组就退化为定长的线性表。反之,n维数组也可以看成是线性表的推广 • 数组中的每个元素都对应于一组下标(j1,…,jk),每个下表的取值范围是0 ji bi-1, bi称为第i维的长度 • 数组一旦被定义,它的维数和维界就不再改变 • 数组结构操作 • 初始化、销毁、元素的存取和修改 • 不过,数组多用于静态数据处理,一般不作插入和删除操作

  5. 数组特点: • 数组中各元素都具有统一的类型 • d维数组的非边界元素具有d个直接前趋和d个直接后继.数组维数确定后,数据元素个数和元素之间的关系不再发生改变,适合于顺序存储 • 每组有定义的下标都存在一个与其相对应的值

  6. a11 0 0 1 • a12 按行序为主序存放 • a11 按列序为主序存放 1 • ……. • a21 • a1n n-1 • ……. • a21 m-1 n • am1 m • a22 a11 a12 …….. a1n a11 a12……..a1n • a12 • …….. a21 a22 …….. a2n a21 a22 ……..a2n • a22 • a2n • …….. …………………. …………………. • ………. • am2 • am1 am1 am2 …….. amn am1am2…….. amn • ………. • am2 • a1n Loc( aij)=Loc(a11)+[(i-1)n+(j-1)]*l • …….. Loc(aij)=Loc(a11)+[(j-1)m+(i-1)]*l • a2n m*n-1 • amn • …….. m*n-1 • amn 数组的顺序表示 • 数组通常采用顺序存储方式来实现 • n维数组的数据元素的存储问题 • 必须约定存放次序 • 因为存储单元是一维的,而数组是多维的 • 存储方案 • 以行序为主序,如C, Pascal, Basic等语言采用 • 以列序为主序,如Fortran语言采用 • 数组一旦定义了维数和各维长度,便可为其分配存储空间 • 只要给出一组下标便可求得相应元素的存储位置

  7. A Loc(0) A[0] 0 L 1 A[1] i A[i] b1-1 A[b1-1] 数据元素的存储问题 • 一维数组为例 • 如 int A[b1], 共占用b1个整型存储单元 • 给定下标值i,求对应元素的存储位置 • Loc(i) = Loc(0) + i * L 数组基址 元素所占存储单元大小

  8. 数据元素的存储问题 • 二维数组为例 • 如 int A[b1,b2],共占用b1*b2个整型存储单元 • 如 行序为主序的存储方式(图a) • 给定下标值i, j,求对应元素的存储位置 Loc(i, j) = Loc(0, 0) + (b2 * i + j) * L A Loc(0,0) A[0,0] A[0,1] A[0,b2-1] A[1,0] A[1,1] A[1,b2-1] A[b1-1,0] A[b1-1,1] A[b1-1, b2-1]

  9. 数据元素的存储问题 • n维数组为例 • 如 int A[b1,b2 ,…,bn],共占用b1*b2 *…*bn个整型存储单元 • 行序为主序的存储方式 • 给定下标值j1, j2,…, jn , 求对应元素的存储位置 Loc(j1, j2,…, jn) = Loc(0, 0,…,0) + L * (b2* …..*bn* j1 + b3* …*bn* j2 + … … bn* jn-1 + jn ) • 考虑3维数组的情形[?]

  10. 矩阵 • 通常使用二维数组来存储矩阵元素 • 矩阵的常见操作 • 转置、相乘等 void TransposeMatrix(int T[][], int M[][], mu, nu) { //矩阵转置 for (col = 1; col <= nu; ++col) for (row = 1; row <= mu; ++row) T[col][row] = M[row][col]; }

  11. 矩阵 void ProductMartrix(int Q[][], int M[][], int N[][], m1, n1, n2) { //矩阵相乘 Qm1*n2=Mm1*n1*Nm2*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]; } }

  12. 矩阵的压缩存储 • 特殊矩阵 • 值相同的元素或零元素在矩阵中的分布有规律 • 如对称矩阵,三角矩阵等 • 稀疏矩阵 • 值相同的元素或零元素在矩阵中的分布无规律,且 • 非零元素个数/矩阵所有元素个数 <= 0.05 • 矩阵的压缩存储 • 多个值相同的元素只分配一个存储空间 • 只存储非零元素

  13. a11 a12 … a1n a21 a22 … a2n … … an1 an2 … ann 特殊矩阵的压缩存储 • n阶对称矩阵 • 矩阵中的元素满足性质:aij = aji • 对称矩阵压缩存储 • 为每一对对称元素分配一个存储空间,这样可将n*n个元素压缩存储到n*(n-1)/2个存储空间中 • 可选择存储其上三角(包括对角线)中的元素或其下三角(包括对角线)中的元素

  14. a11 a12 … a1n a21 a22 … a2n … … an1 an2 … ann A sa a11 a11 0 a12 a21 a22 a1n a31 a21 a32 a22 a33 aij aij k an1 ann an2 n*(n-1)/2 - 1 ann 特殊矩阵的压缩存储 • 讨论以行序为主序的下三角矩阵的存储 • 若不采用压缩存储,矩阵需用二维数组A[n][n]存储 • 若采用压缩存储,可采用一维数组sa[n*(n-1)/2]存储 • 元素sa[k]和矩阵元素aij之间有如下对应关系 i(i-1)/2 + j – 1 当 i >= j k = j(j-1)/2 + i – 1 当 i < j 0 (i-1)*n+j (n-1)*(n-1)

  15. a11 a12 a13 a21 a22 a23 a31 a32 a33 特殊矩阵的压缩存储 • 如3*3对称矩阵 • 未压缩时,用二维数组存放,占用9个单元 • 压缩存放时,用一维数组存放,只需6个单元 • 如a32存放在sa[4]中 • aij = aji sa a11 0 a21 a22 a31 a32 a33 5

  16. 特殊矩阵的压缩存储 • 三角矩阵 • 下(上)三角矩阵是指矩阵的上(下)三角(不包括对角线)中的元均为常数c或零的n阶矩阵 • 三角矩阵的压缩存储 • 只存储下(上)三角中的元素,再加一个存储常数c的空间即可

  17. 特殊矩阵的压缩存储 • 对角矩阵 • 所有非零元素都集中在以主对角线为中心的带状区域中 • 对角矩阵的压缩存储 • 可按照某原则(或以行为主,或以对角线的顺序)将其压缩到一维数组中

  18. 特殊矩阵的压缩存储 • 小结 • 特殊矩阵(如对称矩阵、三角矩阵、对角矩阵等)中,非零元素的分布有明显的规律,因此我们可以将其压缩存储到一维数组中,并找到每个非零元素在一维数组中的对应关系

  19. 稀疏矩阵的压缩存储 • 稀疏矩阵 • 值相同的元素或零元素在矩阵中的分布无规律,且 • 非零元素个数/矩阵所有元素个数 <= 0.05 • 原理 • 只需存储矩阵中的非零元素所在的行号、列号和值 • 方法 • 三元组顺序表 (*) • 行逻辑链接顺序表 • 十字链表(*)

  20. 稀疏矩阵的压缩存储 • 三元组顺序表 • 以顺序结构存储三元组表 #define MAXSIZE 12500 //假设非零元素个数的最大值 typedef struct{ int i, j; //行号,列号 ElemType e;//元素值 }Triple; //三元组 typedef struct{ Triple data[MAXSIZE + 1]; //非零元素三元组表,data[0]未用 int mu, nu, tu; //行数,列数,非零元素个数 }TSMatrix; //三元组顺序表 TSMatrix M; //矩阵M

  21. 稀疏矩阵的压缩存储 • 如稀疏矩阵 • 采用三元组法压缩存储稀疏矩阵 • 按行号排序 • 不支持随机存取,对某行某非零元素访问时,可能需要扫描整个顺序表 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 M.mu = 6 M.nu = 7 M.tu = 8 M = M.data: i j e 1 2 12 1 3 9 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7

  22. 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.mu; ++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; } • 采用三元组顺序表存储的矩阵的转置算法

  23. M.mu = 6 M.nu = 7 M.tu = 8 T.mu = 7 T.nu = 6 T.tu = 8 M.data: T.data: 转置 i j e i j e 1 2 12 1 3 -3 1 3 9 1 6 15 3 1 -3 2 1 12 3 6 14 2 5 18 4 3 24 3 1 9 5 2 18 3 4 24 4 6 -7 6 1 15 6 4 -7 6 3 14

  24. 转置操作:即将A[i][j]改成A[j][i],将NxM矩阵改成MxN矩阵。转置操作:即将A[i][j]改成A[j][i],将NxM矩阵改成MxN矩阵。 0 0 -3 0 0 15 12 0 0 0 18 0 9 0 0 24 0 0 0 0 0 0 0 -7 0 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0 0 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 三元组数组存放的稀疏矩阵转置算法

  25. 算法思想: (1)扫描整个三元组数组,将每个三元组的i和j互换。 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,1,12 0,2, 9 2,0,-3 2,5,14 3,2,24 4,1,18 5,0,15 5,3,-7 1,0,12 2,0, 9 0,2,-3 5,2,14 2,3,24 1,4,18 0,5,15 3,5,-7

  26. 算法思想: (2)原来的三元组数组是按照行序存放的,ij互换后也要调整称为行序存放的方式。这需要事先指导转置后每行非零元素的个数,也就是原矩阵每列非零元素的个数。通过单独扫描一次整个三元组数组,就可以计算得到这些数据。有了这些数据,就可以确定转置后的矩阵的每行的非零元素在数组中的位置。 0 1 2 3 4 5 6 num[i] 2 2 2 1 0 1 0 cpot[i] 0 2 4 6 7 7 8 三元组数组存放的稀疏矩阵转置算法 0,1,12 0,2, 9 2,0,-3 2,5,14 3,2,24 4,1,18 5,0,15 5,3,-7

  27. #define N 50 //行数和列数的最大值 void transmatrix(MATRIX M,MATRIX *T) { int num[N]={0}, cpot[N],p,q,col; //计算转置后矩阵的各行的非零元素的个数 for(p=0;p<M.tu;p++)num[M.data[p].j]++; cpot[0]=0;//开始计算各行非零元素在数组中的起始位置 for(col=1;col<M.nu;col++) cpot[col]=cpot[col-1]+num[col-1]; T->mu=M.nu; T->nu=M.mu; T->tu=M.tu; for(p=0;p<M.tu;p++) { col=M.data[p].j; q=cpot[col]; //q成为该单元在转置矩阵数组中的下标 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 } 三元组数组存放的稀疏矩阵转置算法

  28. 3 0 0 5 0 -1 0 0 2 0 0 0 0 1 2 3 0 1 2 0 1 2 0 3 0 0 1 -1 3 5 2 2、数组 • (4)稀疏矩阵的压缩存储 ②十字链表:三元组的双链表,结点不但包含三元组,而且还包含两个指针,一个称为行链指针,指向下一个同行的非零元素;另一个称为列链指针,指向下一个同列的非零元素。同一个结点,同时链到行链和列链上。 chead rhead

  29. typedef struct olnode { int i,j; int e; struct olnode *right,*down; }OLNODE, *OLink; typedef struct { OLink *rhead,*chead; int mu,nu,tu; }CROSSLIST; 2、数组 • (4)稀疏矩阵的压缩存储 十字链表的数据类型定义:

  30. void creatm(CROSSLIST *M) {int m,n,t,i,j,e; OLink p,q; printf("请输入矩阵的行数、列数和非零元总数\n"); scanf("%d%d%d",&m,&n,&t); M->mu=m; M->nu=n; M->tu=t; //开始为行链和列链的头指针分配空间 M->rhead=(OLink *)malloc((m+1)*sizeof(OLink)); M->chead=(OLink *)malloc((n+1)*sizeof(OLink)); for(i=0;i<m;i++)M->rhead[i]=NULL; for(i=0;i<n;i++)M->chead[i]=NULL; scanf("%d%d%d",&i,&j,&e); //没有处理非法数据 while(i!=-1) //如果i是-1,表示输入结束 {p=(OLNODEPTR)malloc(sizeof(OLNODE)); p->i=i; p->j=j; p->e=e; //下面将p按序插入行链 if(M->rhead[i]==NULL)//行链空,直接插入 {M->rhead[i]=p;p->right=NULL;} else {if(j<M->rhead[i]->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; //插到q的后面 }//else 寻找插入位置 }//else 行链不空 //下面将p按序插入列链 if(M->chead[j]==NULL)//列链空,直接插入 {M->chead[j]=p;p->down=NULL;} else {if(i<M->chead[j]->i) {p->down=M->chead[j]; M->chead[j]=p;} else {for(q=M->chead[j];q->down&&q->down->i<i;q=q->down); p->down=q->down; q->down=p; //插到q的后面 }//else 寻找插入位置 }//else 列链不空 scanf(“%d%d%d”,&i,&j,&e); }//while } 十字链表的创建算法

  31. 作业 • 用以行序为主序和以列序为主序分别写出三维数组A[2][3][4]的元素在内存中的存储次序 • 对上(下)三角矩阵,若采用以行序为主序的原则用一维数组顺序存储其所有非零元素,试找出每个非零元素aij在一维数组中的对应关系

  32. 思考题 • 若在m*n的矩阵中存在一个元素A[i],并满足:A[i,j]是第i行元素中的最小值,且是第j列元素中的最大值,则称矩阵A有鞍点。试写一个算法,找出矩阵A的一个鞍点,若不存在鞍点,则返回某种信息

More Related