4.4.1 数组的定义
数组:
- 按照一定格式排列起来的,具有相同类型的数据元素的集合。
一维数组:
- 若线性表中的数据元素为非结构的简单元素,则称为一维数组。
- 逻辑结构:线性结构,固定长度的线性表。
- 声明格式:数据类型 变量名 [数组长度],如:int A [20],学过C的都知道,就不多赘述了。
二维数组:
-
若一维数组中的数据元素又是一维数组结构,则称为二维数组。
-
逻辑结构:
- 非线性结构:每一个数据元素既在一个行表中,又在一个列表中。
- 线性结构:该线性表的每个数据元素也是一个固定长度的线性表。
除了4个角落的元素之外,每个元素都有不止一个前趋和后继,比如说这里的 a11,在它所在的这一行有前趋 a10 和后继 a12,在它所在的这一列里,又有 a01 这个前趋和 。。。这个后继(好吧图里没有,不过也没差)。 -
声明格式:数据类型 变量名[行数][列数],如 int A [3][4],这样就定义了一个名为A的数组内每个元素都是 int 类型的三行四列的二维数组出来了。
数组特点:
- 结构固定,定以后,维数和维界不再改变。
数组基本操作:
- 除了结构的初始化和销毁之外,只有取元素合修改元素值的操作。
- 不会说有什么插入或者删除数组中的某个元素的操作。
n维数组的抽象数据类型
- n 为数组的维数(1维数组、二维数组…)。
- bi 为数组第 i 维的长度。
- ji 为数组元素第 i 维的下标。
4.4.2 数组的顺序存储
数组特点:
- 结构固定,固定以后,维数和维界不再改变,所以比较适合采用顺序存储结构。
数组基本操作:
- 除了结构的初始化和销毁之外,只有取元素合修改元素值的操作。
- 不会说有什么插入或者删除数组中的某个元素的操作。
注意:
- 数组可以是多维的,但存储数据元素的内存单元地址是一维的,因此,在存储数据结构之前,需要解决将多维关系映射到一维关系的问题。
一维数组
例如:int a [5],每个元素占用4个字节,假设a[0]存储在2000单元的位置,a[3] 应该在 2012 的位置上,2000 + (3 - 0)*4。
下标为 3 的元素前面有 3 个元素,如果是下标为 i 的元素,则前面有 i 个元素,第 i 个元素的位置应该是首元素的地址+i * 每个元素的大小——>LOC(i) = a + i * L 。
二维数组
假设一个 m 行 n 列的二维数组,每一行都有 n 个元素,每列都有 m 个元素。
这样一个二维数组可以看成是由若干行组成的,也可以看成是由若干列组成的。
二维数组有两种存储方式:
- 以行为主序(低下标优先)BASIC、COBOL 和 PASCAL
- 以列为主序(高下标优先)FORTRAN
以行序为主序
- 按照行来存储,存储完第一行之后再存储第二行,以此类推。
- 要查找某个元素的话就要找到对应的行列位置,如:第一行一列的元素就在[0][0]的位置,第二行第二列的元素就在下标为[1][1]的位置。
每一行都从第一个元素开始存储直到存储到下标为 n - 1 的位置然后就换下一行存储。第一行的首元素下表[0][0],最后一行的首元素下标则是[m - 1][0]
以行序为主序的存储位置计算
- 设数组开始存储位置为LOC(0,0),存储每个元素需要 L 个存储单元(字节)。
- 数组元素 a[i][j] 的存储位置是:LOC(i,j) = LOC(0,.0) + (n * i + j) * L
以列序为主序
- 按照列来存储,存储好一列之后再存储下一列。
先从第一列开始存,第一列的首元素存到最后一个元素的下标应该是 a[0][0](首行首列),a[1][0](二行一列)a[2][0])(三行一列)以此类推直到 a[m - 1][0](m行1列)。
4.4.3 特殊矩阵的压缩存储
矩阵
- 一个有 m * n 个元素排成的 m 行 n 列的表。
- 有时为了节省存储空间,可以对这类矩阵进行压缩存储。
压缩存储
:
- 为多个值相同的元素只分配一个存储空间,对零元素不分配空间。
- 如:相片的压缩之类的。
矩阵的常规存储:
- 将矩阵描述为一个二维数组。
- 特点:
- 可以对其元素进行随机存取。
- 矩阵运算非常简单,存储的密度为1(不需要额外的空间存放地址,分配100个字节的空间就能存储100字节的数据)。
适合压缩存储的矩阵:
- 值相同的元素很多且呈某种规律分布;零元素多。
- 如:对称矩阵、对角矩阵、三角矩阵、稀疏矩阵等。
什么是稀疏矩阵
- 矩阵中非零元素的个数较少(一般小于 5%)
- 有 95% 以上的元素是零,不需要给这些元素分配空间,一下节省 95% 的空间。
对称矩阵
沿着对角线相等的矩阵就称为对称矩阵
特点:
- 在 n * n 的矩阵 a 中,满足如下性质:aij = aji (1 <= i,j <= n)。
- 比如说上图中 a[4][1] = a[1][4]
存储方式:
- 因为元素是按照对角线来存储的,另一半是一样的元素,不需要全部存,只存储下(或者上)三角(包括主对角线)的数据元素。
- 共占用 n(n + 1) / 2 ,项数 *(首项 + 末项)/ 2个元素空间。
对称矩阵的存储结构
- 对称矩阵上下三角中的元素数均为:n(n + 1) / 2。
- 可以
以行序为主序
将元素存放在一个一维数组 sa[n(n + 1) / 2]中。
下标用 k 来表示,第一行放1个元素进a[0]中,第二行放两个进a[1],a[2]中,以此类推。
三角矩阵
以主对角线划分,三角矩阵有上三角矩阵和下三角矩阵两种。上三角矩阵是指矩阵下三角(不包含对角线)中的元素均为常数 C (所有值相等) 或零的 n 阶矩阵,下三角矩阵与之相反。对三角矩阵进行压缩存储时,除了和对称矩阵一样,只存储上(下)三角中的元素之外,再加一个存储常数 C 的空间即可。
特点:
- 对角线以下(或以上)的数据元素(不包括对角线)全部为常数 C (所有值一样)。
存储方式:
- 重复元素 C 共享一个元素存储空间,总共占用 n(n + 1) / 2 + 1个元素空间:sa[1…n(n+1) / 2+1]。
- 上三角矩阵 sa[k] 和矩阵元 aij 之间的对应关系为
- 下三角矩阵 sa[k] 和矩阵元 aij 之间的对应关系为
对角矩阵(带状矩阵)
对角矩阵所有的非零元素都集中在以主对角线为中心的带状区域中,即除了主对角线上和直接在对角线上、下方若干条对角线上的元之外,所有其他的元素皆为零。
特点
- 在 n * n 的方阵中,所有非零元素都集中在以主对角线为中心的带状区域中(只在对角线和对角线旁边有值),区域外的值全是 0 ,则称为对角矩阵。
- 常见的有三对角矩阵、五对角矩阵、七对角矩阵。
- 如:一个 7 * 7 的三对角矩阵,只有在三条对角线的范围内有值。再加两条有值的对角线就是五对角矩阵了,依次类推。
存储方式
- 为 0 的元素直接扔掉不存。
- 用二维数组的方式去存。
按对角线来存储,第一条对角线是 3385,则将3385存在二维数组的第一行,第二条对角线是 20612 则存在第二行,以此类推,以主对角线为0进行上下对称。这样一个原本需要36个存储空间的矩阵现在就只需要30个了。
稀疏矩阵
什么是稀疏矩阵
- 矩阵中非零元素的个数较少(一般小于 5%)
- 有 95% 以上的元素是零,不需要给这些元素分配空间,一下节省 95% 的空间。
在这样有42个元素的矩阵中非零元素只有8个,如果将零也存储起来,就非常浪费空间了,存储密度只有 19.05%左右。
存储方法
:
- 三元组,每个非零元素由它所在的行列和它本身的值来确定,(i,j,aij)。,比如下图的第一个元素12,它的存储就是1行二列值12确定。
- 压缩存储原则:存各非零元素的值,行列位置和矩阵的行列数。
- 三元组的不同表示方法可以决定稀疏矩阵不同的压缩存储方法。
第0行表示存储的这个矩阵有几行几列几个非零元素,其余每行就是按顺序存储非零元素的行列值了。
还原稀疏矩阵
试还原出下列三元组所表示的系数矩阵。
- 第一行的 646 表示,原来的系数矩阵有 6行4列6个非零元素。
- 非零值按照 i 和 j 所代表的行列位置以及将value值存放到具体位置,如:第二行的 122 表示值为2的元素出现在1行2列的位置,其余同理。
- 其余所有位置全部补 0 。
三元组顺序表的优缺点
- 优点:非零元素在表中按行执行有序存储,因此便于进行依行顺序处理的矩阵运算。
- 缺点:缺点同样是因为按行处理的,导致不能随机存取。若按行号存取某一行中的非零元,则需要从头开始进行查找。这个时候就需要用到稀疏矩阵的链式存储结构:十字链表了。
十字链表
-
优点:能够灵活地插入因运算而产生的新的非零元素,删除因运算而产生的新的零元素,实现矩阵的各种运算。
-
在十字链表中,矩阵的每一个非零元素用一个结点表示,该结点除了(row,col,value)以外,还要有两个域。
- right :用于连接同一行中的下一个非零元素。
- down:用于连结同一列中的下一个非零元素。
-
十字链表中结点的结构示意图:
假设有这样一个矩阵 m
-
先要存储第一个非零元素 3 ,需要知道与它同一行的下一个元素5的位置,以及同列的下一个元素2的位置。
-
此时还需要1个结点来存储 -1,同样,因为与 -1 同行同列的已经没有非零元素了,所以 right 及 dawn 域都置空。
此时还需要两个分别指向每行每列的头指针数组,在这样一个十字链表中,有三行,所以就需要弄一个存储三个头指针的数组,列同理。
再举个栗子