数组和特殊矩阵
一、数组的存储结构
我们一般定义一个一维数组a[n],如果知道它的起始地址,那就一定知道它的其他地址,因为各数组元素大大小相同,并且在物理上连续存放
。
比如如果我们起始地址是c0,一个数组元素占的空间为x这么大
那我们的第i个元素的存放地址就是c0+(i-1) * x,a[i]的存放地址就是c0+i * x(数组下标默认0开始)。
那如果是二维数组呢?
我们一般定义一个二维数组,比如b[2][4],这就是个二行四列的二维数组。
逻辑视角是这样的:
b[0][0] | b[0][1] | b[0][2] | b[0][3] |
---|---|---|---|
b[1][0] | b[1][1] | b[1][2] | b[1][3] |
可以看到如果我们要存储的话,那就有了两种方式:按行和按列,也就是行优先存储和列优先存储。
其实就是字面意思,行优先存储就是先把一行存了再存下一行,这样一行一行来,列优先存储就是先把一列存了再存下一列,这样一列一列来(其实都是把它们展开成一排这样),所以M行N列的二维数组b[M][N]中,
若按行优先存储,则
b[i][j]的存储地址=c0+(i*N+j)*x
要想知道b[i][j]的存储地址,就看它前面有几个,比如b[0][1]的存储地址为c0+x(因为它前面只有一个b[0][0])。逻辑空间里b[i][j]上面有i行完整的N,左边还有j个元素,所以是c0+(i*N+j)x.
若按列优先存储,则
b[i][j]的存储地址=c0+(j*M+i)*x
与上同理,逻辑空间里b[i][j]左边有j列完整的M,上面还有i个元素,所以是c0+(j*M+i)*x.
二、特殊矩阵
一般我们所说的矩阵就很像一个二维数组,所以也可以用二维数组存储。但是遇到某些特殊的可以不一个一个全部存储,不然也太浪费存储空间了。
还有就是,描述矩阵元素时,行、列的下标通常从1开始(我们线代学过,第一行第一列元素是a1,1这样)。
注:以下说的除了稀疏矩阵,其他都是n×n的方针。
1.对称矩阵
所谓对称矩阵,就是n阶方阵中任意一个元素ai,j都有ai,j=aj,i.
那我们可以把这个方阵分为三个区域,从左上角到右下角画一条线,主对角线(i=j)是一个区域,上三角区(i<j)是一个区域,下三角区(i>j)是一个区域。
我们又知道ai,j=aj,i,所以上三角区和下三角区每个元素都和主对角线作垂线是一一对应的,故只需要存储主对角线+下三角区(或主对角线+上三角区)就可以了。那么我们存储的大小是多少呢?第一行1个,第二行2个…第n行n个,所以大小为n*(n+1)/2个。
现在我们只存储主对角线+下三角区,我们上面说过存储二维数组既可以行优先也可以列优先,所以我们对称矩阵用二维数组也是。
首先是按行优先
原则存储,我们存到一个B[n*(n+1)/2-1]的数组内,我们怎么知道ai,j对应的B[k]中k是什么?这时我们就得需要实现一个矩阵下标->一维数组下标的“映射”函数。
行优先的话,ai,j是第几个元素?我们下三角区的ai,j,上面有i-1行,这i-1行,一共有1+2+…+i-1个元素,即i(i-1)/2个。除此之外,ai,j它当行前面有j-1个元素。
所以行优先中,ai,j是第i(i-1)/2+j个元素,对应的B[k]中k就是i(i-1)/2+j-1(第一个元素对应的是B[0]).
首先是按列优先
原则存储,列优先的话,ai,j是第几个元素?要求ai,j是第几个元素,我们只要知道aj,i是第几个元素就可以了。因为ai,j=aj,i嘛。
所以列优先中,ai,j是第j(j-1)/2+i个元素,对应的B[k]中k就是j(j-1)/2+i-1(第一个元素对应的是B[0]).
但是我们做题的时候,需要注意的就是一些小细节,比如题目说的是上三角还是下三角呀,是行优先还是列优先呀,矩阵元素的下标和数组元素的下标是从0开始还是从1开始呀啥的。
2.三角矩阵
三角矩阵分为下三角矩阵和上三角矩阵。
下三角矩阵就是除了主对角线和下三角区域,其余的元素都相同的矩阵
上三角矩阵就是除了主对角线和上三角区域,其余的元素都相同的矩阵。、
2.1下三角矩阵
我们存下三角矩阵,是按行优先的原则将下三角元素存入一维数组中,并在最后一个位置存储常量c(上三角区域的元素都一样),所以我们一维数组需要的大小就是1+2+…+n+1,即n*(n+1)/2+1个,最后一个存储常量的位置是B[n*(n+1)/2]。
所以上三角区元素ai,j(i<j)对应的一维数组的下标就是n*(n+1)/2。
下三角区域中,我们ai,j上面有i-1行,这i-1行,一共有1+2+…+i-1个元素,即i(i-1)/2个。除此之外,ai,j它当行前面有j-1个元素。
所以下三角区中,ai,j(i>j)是第i(i-1)/2+j个元素,对应的B[k]中k就是i(i-1)/2+j-1。
2.2上三角矩阵
显然我们上三角矩阵需要的一维数组大小也是n+n-1+…+1+1,即n*(n+1)/2+1个,最后一个存储常量的位置是B[n*(n+1)/2]。
那么上三角区域中,我们ai,j(i<j)上面有n+…+n-i+2行,ai,j左边有j-i列
所以上三角区中,ai,j是第i(i-1)(2n-i+2)/2+j-i+1个元素,对应的B[k]中k就是i(i-1)(2n-i+2)/2+j-i,下三角区元素ai,j(i>j)对应的一维数组的下标就还是n*(n+1)/2。
等差数列和Sn=N*(a1+an)/2 ,N为数列中多少项。
3.三对角矩阵
三对角矩阵,人如其名,只有三个对角线有值的矩阵。那哪三个对角线有值?就是主对角线一个,它紧邻的上面对角线一个,紧邻的下面对角线一个。其他没值的就是0.
三对角矩阵,又称带状矩阵:
当|i-j|>1时。有ai,j=0(1≤i,j≤n)
所以我们只需要存储带状部分就好了。那我们要多少的存储空间?首先这个矩阵中,画横线,大多行都有三个元素,除了第一行和最后一行。那我们就知道了,这个矩阵里面一共有3n-2个元素(因为第一行最后一行每行少一个嘛),所以我们需要滴存储空间就是3n-2个,存到B[0]~B[3n-3]中。
那么我们按照行优先存储,这个带状矩阵它的ai,j是第几个元素?
要想知道这个,首先得看前i-1行一共有多少个元素。前i-1行除了第一行每行都有3个元素,所以前i-1行一共有3(i-1)-1个元素。知道了这个之后,我们还得知道ai,j是第i行的第几个元素(因为我们存储数据肯定是只存有数据的那三个对角线上的,我们对应的也是只存这三个对角线的情况,所以ai,j只可能是第i行的第1,2,3个元素。具体是第几个就要看它的j-i的值,如果是-1就是第一个,0就是第二个,1就是第三个),是第j-i+2个元素;知道了这个之后我们就能确定,这个带状矩阵它的ai,j是第(2i+j-1)个元素,对应的数组下标k就是(2i+j-3)
反过来,如果我们知道数组下标k,怎么得到i,j?即我们得知道第k+1个元素在矩阵的第几行第几列。
我们已经知道了前i-1行一共有3(i-1)-1个元素,前i行一共有3i-1个元素,那么显然,3(i-1)-1<k+1≤3i-1(右边有个等于因为我们假设这个矩阵元素是在第i行)
所以i≥(k+2)/3,刚好大于等于的话可以向上取整,即i=⌈(k+2)/3⌉
向上取整即可满足“刚好”大于等于
我们不只要求行,还要求列,但我们之前不是已经知道k=2i+j-3了吗,所以刚刚我们求到i的值之后就可以得到j了,我们的j就是k-2i+3.
4.稀疏矩阵
稀疏矩阵就是矩阵中的元素都是零零散散的,非零元素远远少于矩阵元素的个数。
所以我们要存的话可以用顺序存储,也可以用链式存储。
顺序存储
就是-三元组<行,列,值>,这样存储的话我们1可以定义一个结构体,里面存放i(行),j(列),v(值),每一个结构体是一个元素。
链式存储
又称十字链表法,由于不太好描述所以我就查了一张图,我的保姆级教程一般都是靠文字叙述的,因为靠自己在脑子里想会更有印象。这个十字链表法就是每一个结点存放的不只有它的行号、列号和值,还有就是它当前行的下一个有元素的结点,当前列下一个有元素的结点。这样的话更加的方便插入删除,易于维护。
总结
这些特殊数组主要就是怎么根据矩阵的行号列号推出对应的数组下标号还有倒推,但是要注意的就是到底是行有线还是列优先,矩阵和数组元素的下标是从0还是1开始。