第 4 章 数组和广义表

23
4 4 第 第第第第第第 第 第第第第第第

Upload: rufina

Post on 22-Jan-2016

69 views

Category:

Documents


3 download

DESCRIPTION

第 4 章 数组和广义表. 4.1 多维数组. 4.1.1 数组定义 数组是数据结构的基本结构形式,它是一种顺序式的结构,数组是存储同一类型数据的数据结构,使用数组时需要定义数组的大小和存储数据的数据类型,数组分为一维数组和多维数组。数组的维数是由数组的下标的个数确定的,一个下标称为一维数组,一个下标以上的数组称为多维数组。从这个意义上讲,确定了对于数组的一个下标总有一个相应的数值与之对应的关系;或者说数组是有限个同类型数据元素组成的序列。 数组的基本操作包括: initarray ( &A ); // 初始化数组 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第 4 章 数组和广义表

第第 44 章 数组和广义表 章 数组和广义表

Page 2: 第 4 章 数组和广义表

4.1 4.1 多维数组 多维数组 • 4.1.1 数组定义

– 数组是数据结构的基本结构形式,它是一种顺序式的结构,数组是存储同一类型数据的数据结构,使用数组时需要定义数组的大小和存储数据的数据类型,数组分为一维数组和多维数组。数组的维数是由数组的下标的个数确定的,一个下标称为一维数组,一个下标以上的数组称为多维数组。从这个意义上讲,确定了对于数组的一个下标总有一个相应的数值与之对应的关系;或者说数组是有限个同类型数据元素组成的序列。

– 数组的基本操作包括:– initarray ( &A ); // 初始化数组– destroyarray ( &A ); // 销毁数组– assign ( &A , e ); // 数组赋值– value ( A , &e ); // 取数组的某个元素 – copyarray ( M , &T ); // 复制一个数组– printarray ( M ); // 打印数组的元素

Page 3: 第 4 章 数组和广义表

4.1 4.1 多维数组多维数组

• 4.1.1 数组定义• 一维数组

– 一维数组是指下标的个数只有一个的数组,有时称为向量,是最基本的数据类型,在 java 中需要事先声名,程序才能够在编译过程中预留内存空间。声明的格式一般是:

– < 数据类型 > < 变量名称 > [ ]= new < 数据类型 > [< 数组大小 >] ;

Page 4: 第 4 章 数组和广义表

4.1 4.1 多维数组多维数组

• 4.1.1 数组定义• 多维数组

– 多维数组是指下标的个数有两个以上,我们比较常用的是二维数组(因为三维以上的数组存储可以简化为二维数组的存储)。下面以二维数组为例说明多维数组。二维数组的声明同一维数组。格式为:

– < 数据类型 > < 数组名称 > [ ] [ ]=new < 数据类型 >[size1] [ size2];

Page 5: 第 4 章 数组和广义表

4.1 4.1 多维数组多维数组• 4.1.2 数组的存储• 一维数组的存储

– 一维数组的数据存储按照顺序存储,逻辑地址和物理地址都是连续的。如果已知第一个数据元素的地址 loc(a1) ,则第 i 个元素的地址loc(ai) 为:

– loc(ai)=loc(a1)+ ( i-1 ) *c– 假设数组的下标从 1 开始,只要求出第 i 个元素之前存放了多少个

数据元素即可(实际上有 i-1 个元素),每个元素占有 c 个存储单元,再乘以 c ,就是第 i 个元素的起始地址。

– 如果下标从 0 开始,则第 i 个元素之前就有 i 个元素,此时上面的公式就变为:

– loc(ai)=loc(a1)+ i*c– 由此可见,求数组中数据元素的地址,已知条件必须是知道第一个

元素的地址,然后主要是找出该元素之前已经存储了多少个数据元素。在一维数组中,只要知道任何一个元素的地址即可求出其它元素的地址,但在多维数组中,已知条件必须是第一个数据元素地址。

Page 6: 第 4 章 数组和广义表

4.1 4.1 多维数组多维数组• 4.1.2 数组的存储• 多维数组

– 以二维数组的顺序存储为例说明,二维数组在顺序存储时一般有两种:

• 行优先顺序:存储时先按行从小到大的顺序存储,在每一行中按列号从小到大存储。

• 列优先顺序:存储时先按列从小到大的顺序存储,在每一列中按行号从小到大存储。

– 以上的两种存储顺序中,第一个被存放的元素总是第一行第一列的数据元素,所以该元素的地址是我们的已知条件。

– 同样在二维数组中比较典型的是计算数据的存储位置。

Page 7: 第 4 章 数组和广义表

4.1 4.1 多维数组多维数组• 4.1.2 数组的存储• 多维数组

– 假设二维数组是 m*n 的二维数组(共有 m 行,每行有 n列)。第一个数据元素的地址是 loc(a11) ,则第 i 行第 j 列的数据元素的地址的计算公式应为(按照行优先顺序存储):

– loc(aij)=loc(a11)+[ ( i-1 ) *n+j-1]*c– 假设下标从 1 开始,我们需要计算出 i 行前面已经存储了 i-1

行元素,每行有 n 个元素,共有( i-1 ) *n 个数据元素,在第 i 行元素中, j 列之前有 j-1 个数据元素,共有( i-1 ) *n+j-1 个元素,每个元素占有 c 个存储单元,只要乘以 c 就可以了。其中 loc(aij) 表示第 i 行第 j 列数据元素的内存的起始位置, loc(a11) 表示第一个数据元素的内存位置, c 表示每个数据元素所占有的内存空间的大小,如果下标从 0 开始,只要不用减 1 即可。

Page 8: 第 4 章 数组和广义表

4.1 4.1 多维数组多维数组• 4.1.2 数组的存储• 多维数组

– 如果按列优先顺序存储,则地址的计算为:– loc(aij)=loc(a11)+[ ( j-1 ) *m+i-1]*c– 假设下标从 1 开始,其中 loc(aij) 表示第 i 行第 j 列的数据元素的内

存起始位置, loc(a11) 表示第一个数据元素的内存位置, c 表示每个数据元素所占有的内存空间的大小;主要还是计算第 i 行 j 列元素之前有多少个数据元素。如果下标从 0 开始,只要不用减 1 即可。

– 按此公式可以推广到多维数组的数据元素的地址计算(假设按照行优先顺序存储):

– m 行 n 列纵标为 k 的三维数组,假设第一个元素的地址是 loc(a111) ,如果按行优先顺序存储, i 行 j 列纵标为 p 的数据元素的地址为(可以将它分解为二维数组):

– loc(aijp)=loc(a111)+[(i-1)*n*k+(j-1)*k +p-1]*c;– 如果下标从 0 开始,只要不用减 1 即可。– 读者可以从以上的地址公式中可以找出一定的地址计算规律:多维

数组中按行优先计算公式用一个下标乘以后面的最大值。

Page 9: 第 4 章 数组和广义表

4.1 4.1 多维数组多维数组

• 4.1.3 显示二维数组的内容– 一般情况下,只要定义了数组的存储顺序,

数组的存储顺序就不会改变了,所以对数组的各种操作后,应按照数组的已定义的存储顺序存储;也就是说,如果是按行优先顺序存储,在对数组操作后,即使改变了存储顺序,应加以改变仍然按照行优先顺序存储。

Page 10: 第 4 章 数组和广义表

4.2 4.2 矩阵的压缩存储 矩阵的压缩存储

• 4.2.1 矩阵的压缩存储– 所谓矩阵的压缩存储,也就是在存储数组时,尽量

减少存储空间,但是数组中的每个元素必须存储,所以在矩阵存储中,如果有规律可寻,只要存储其中一部分,而另一部分的存储地址可以通过相应的算法将它计算出来,从而占有比较少的存储空间达到存储整个矩阵的目的,称为矩阵的压缩存储。

– 矩阵的压缩存储仅是针对特殊矩阵的;而对于没有规律可循的二维数组则不能够使用压缩存储。

– 二维数组(矩阵)的压缩存储一般有三种,它们分别是对称矩阵、稀疏矩阵和三角矩阵。三种矩阵中以稀疏矩阵比较常见。

Page 11: 第 4 章 数组和广义表

4.2 4.2 矩阵的压缩存储 矩阵的压缩存储

• 4.2.1 矩阵的压缩存储• 特殊矩阵

– 若 n 阶矩阵 A 中的元素满足以下条件:– aij=aji i≥1 , j≥1– 则称为 n 阶对称矩阵。– 对于对称矩阵,如果不采用压缩存储,占有的存储

单元有 n2 个,因为是对称矩阵,所以只要存储对角的数据元素和一半的数据元素即可,占有的存储单元有 n ( n-1 ) /2 个存储单元中。如果我们以行序为主序存储其下三角(包括对角线)的元素,其上三角的元素可以推算出来。

Page 12: 第 4 章 数组和广义表

4.2 4.2 矩阵的压缩存储 矩阵的压缩存储

• 4.2.1 矩阵的压缩存储• 特殊矩阵

– 如果用一维数组存储一个对称矩阵,只要将对称矩阵存储在一个最大下标为 n ( n-1 ) /2 的一维数组 S 中即可。此时按照行优先顺序存储,数据元素 aij 与数组 S 的下标 k 的对应关系为:

i(i-1)/2 +j-1 当 i≥j 时 k= j(j-1)/2 + i-1 当 i < j 时

Page 13: 第 4 章 数组和广义表

4.2 4.2 矩阵的压缩存储 矩阵的压缩存储

• 4.2.1 矩阵的压缩存储• 特殊矩阵

– 对于任意给定的一组下标( i , j ),均可在 S 中找到元素 aij ,反之,对所有元素都能够确定在 S中位置,当 i < j 时,根据对称矩阵的性质推算即可。由此可以看出对称矩阵的存储可以使用一维数组 S 存储,占用的空间不再是 n2 ,而是 n ( n-1 ) /2 空间减少了接近一半,实现了二维数组的压缩存储。

– 所谓对角矩阵是指,矩阵的所有非零元素都集中在以主对角线为中心的带状区域中,即除了主对角线上和直接在主对角线上、下方若干条对角线上的元素之外,其余元素皆为零。

Page 14: 第 4 章 数组和广义表

4.2 4.2 矩阵的压缩存储 矩阵的压缩存储 • 4.2.1 矩阵的压缩存储• 特殊矩阵

– 也可以按照某个原则(或者以行序为主序,或者以列序为主序,或者按对角线的顺序)将对角矩阵 B 的所有非零元素压缩存储到一个一维数组 LTB[1…3n-2] 中。这里不妨仍然以行序为主序的原则对 B 进行压缩存储,当 B 中任一非零元素bij 与 LTB[k] 之间存在着如下一一对应关系:

– k=2*i+j-2– 时,则有 bij=LTB[k] 。称 LTB[1…3n-2] 为对角矩阵 B 的压

缩存储。– 上面讨论的几种特殊矩阵中,非零元素的分布都具有明显的

规律,因而都可以被压缩存储到一个一维数组中,并能够确定这些矩阵的每个非零元素在一维数组中的存储位置。但是,对于那些非零元素在矩阵中的分布没有规律的特殊矩阵 ( 如稀疏矩阵 ) ,则需要寻求其他方法来解决压缩存储问题。

Page 15: 第 4 章 数组和广义表

4.2 4.2 矩阵的压缩存储 矩阵的压缩存储 • 4.2.1 矩阵的压缩存储• 稀疏矩阵

– 对稀疏矩阵很难下一个确切的定义,它只是一个凭人们的直觉来理解的概念。一般认为,一个较大的矩阵中,零元素的个数相对于整个矩阵元素的总个数所占比例较大时,该矩阵就是一个稀疏矩阵。例如,有一个 6×6 阶的矩阵 A ,其 36 个元素中只有 8 个非零元素,那么,可以称矩阵 A 为稀疏矩阵。

– 稀疏矩阵一般是指矩阵中的大部分元素为零,仅有少量元素非零的矩阵称为稀疏矩阵;或者说矩阵 A( m × n )中有 S 个非零元素,如果 S远远小于矩阵的元素总数,称 A 为稀疏矩阵。稀疏矩阵的存储一般只要保存非零元素即可,对于零元素可以不与保存,这样可以实现稀疏矩阵的压缩存储。

Page 16: 第 4 章 数组和广义表

4.2 4.2 矩阵的压缩存储 矩阵的压缩存储 • 4.2.1 矩阵的压缩存储• 稀疏矩阵

– 稀疏矩阵的压缩存储采用三元组的方法实现。其存储规则如下:

– 每一个非零元素占有一行,每行中包含非零元素所在的行号、列号、非零元素的数值。为完整描述稀疏矩阵,一般在第一行描述矩阵的行数、列数和非零元素的个数。其逻辑描述为:

– ( row col value )– 其中 row 表示行号, col 表示列号, value 表示非零元素的

值。– 如果每个非零元素按照此种方法存储,虽然能够完整地描述

非零元素,但如果矩阵中有整行(或整列)中没有非零元素,此时可能不能够还原成原来的矩阵,所以为了完整地描述稀疏矩阵,在以上描述的情况下,如果增加一行的内容,该行包括矩阵的总的行数、矩阵的总的列数,矩阵中非零元素的个数,就可以还原为原来的矩阵描述了。

Page 17: 第 4 章 数组和广义表

4.2 4.2 矩阵的压缩存储 矩阵的压缩存储

• 4.2.1 矩阵的压缩存储• 稀疏矩阵

– 归纳起来,若一个稀疏矩阵有 t 个非零元素,则需要用 t+1 行的三元组来表示稀疏矩阵。到底矩阵何时使用三元组存储呢?一般对m×n 的矩阵来说,只要满足( t+1 ) *3≤m*n 这个条件,使用三元组存储可以节省空间,否则更加浪费空间,也就没有必要使用三元组存储,所以稀疏矩阵中的非零元素的个数 t 是能否使用三元组存储的关键。

Page 18: 第 4 章 数组和广义表

4.2 4.2 矩阵的压缩存储 矩阵的压缩存储

• 4.2.2 稀疏矩阵转换为三元组存储 – 首先应该将稀疏矩阵转换为三元组存储,然后才利

用三元组的存储,实现对矩阵的各种运算。– 对于矩阵的运算一般有矩阵的转置,在转置时值得

注意的是:在矩阵的存储规则已经确定的情况下(如按行优先存储),实现矩阵的运算(如转置)时,应仍然保留原来的存储规则。

– 改进的转置方法可以利用对原始的三元组的元素的扫描,直接确定该元素在转置后的三元组中的行,这样可以将原始三元组中的元素直接放在转置后的三元组中即可。这种方法需要增加两个一维数组的结构开销,称为快速转置。

Page 19: 第 4 章 数组和广义表

4.3 4.3 广义表 广义表

• 4.3.1 广义表的定义– 广义表是线性表的扩展,具体定义为 n ( n

≥0 )个元素的有限集合。其中元素有以下两种类型:

• 1 )一个原子元素(指不可再分的元素);• 2 )一个可以再分的元素(或称为一个子表)。

– 如果所有元素都是原子元素,则称为线性表,如果含有子表则是广义表。

Page 20: 第 4 章 数组和广义表

4.3 4.3 广义表 广义表 • 4.3.1 广义表的定义

– 广义表的基本操作:– initGlist(&L) // 创建空的广义表– creatGlist(&L,S) // 由 S 创建广义表 L– destroyGlist(&L) // 销毁广义表 L– Glistlength ( L ) // 求广义表的长度– Glistdepth ( L ) // 求广义表的深度– Gethead(L) // 求广义表 L 的头– Gettail(L) // 求广义表的表尾– Insertfirst_Glist(&L,e) // 插入元素 e 作为广义表 L 的第一个

元素– Deletefirst_Glist(&L,&e) // 删除广义表 L 的第一个元素,并

用 e 返回其值

Page 21: 第 4 章 数组和广义表

4.3 4.3 广义表 广义表 • 4.3.1 广义表的定义

– 广义表一般记作:– LS= ( a1 , a2 , …, an )– 其中 LS 是广义表的名称, n 是广义表的长度。– 常见的广义表为:– A= ()– B= (())– C= ( a,b )– D= ( A , B , C )– E= ( a,E )– 广义表中含有元素的个数称为广义表的长度,广义

表中含有的括号对数称为广义表的深度。

Page 22: 第 4 章 数组和广义表

4.3 4.3 广义表 广义表

• 4.3.1 广义表的定义• 三个重要结论:

– 列表的元素可以是子表,而子表的元素还可以是子表…。由此,列表是一个多层次的结构,可以用图形象地表示。例如图 4-1 表示的是列表 D 。图中以圆圈表示列表,以方块表示原子元素。

– 列表可为其它列表所共享。例如在上述例子中,列表 A、 B 和 C 为 D 的子表,则在 D 中可以不必列出子表的值,而是通过子表的名称来引用。

– 列表可以是一个递归的表,即列表也可以是其本身的一个子表。例如列表 E 就是一个递归的表。

Page 23: 第 4 章 数组和广义表

4.3 4.3 广义表 广义表

• 4.3.2 广义表的存储– 广义表的存储方法有很多种,一般

采用链表存储。采用链表存储时的结点存储的逻辑结构(如图所示)一般是:

– 其中 flag 表示标志位,当 flag 为 0时,该结点表示原子元素,当 flag为 1 时,该结点表示子表;当 flag为 0 时, info 表示原子元素的值,当 flag 为 1 时, info 表示指针,指向该子表的第一个结点; link 表示指针,指向广义表的下一个元素。

Flag info link