此文章是本人第一篇博客,目的在于巩固过去所学的知识,同时可能会给大家带来一丝丝帮助,但由于没有经验加上本人能力极其有限,文章中可能存在不足之处,还请读者能够指正(`・ω・´)。
这篇文章首先会介绍矩阵压缩存储和特殊矩阵的基本概念,然后将会比较详细地说明几种特殊的矩阵:对称矩阵、三角矩阵、带状矩阵(三对角矩阵)、稀疏矩阵的压缩存储的策略,包括定量计算和十字链表的c语言代码和效果图,让我们开始吧!
注意,本文所用数组下标均为从0开始
在一个矩阵中,若多个数据的值相同,为了节省内存空间,则只分配一个元素值的空间,且零元素不占存储空间
可不是所有的矩阵都适合压缩存储!比如下面这个
1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
下面介绍的几个特殊矩阵,才是将要重点介绍的对象:
1.对称矩阵
对称矩阵是指元素以主对角线为对称轴对应相等的n阶矩阵,即aᵢ,ⱼ=aⱼ,ᵢ
比如
5 | 1 | 2 | 3 | 4 |
1 | 5 | 6 | 7 | 8 |
2 | 6 | 5 | 9 | 10 |
3 | 7 | 9 | 5 | 12 |
4 | 8 | 10 | 12 | 5 |
2.三角矩阵
三角矩阵是方阵的一种,分为上三角矩阵和下三角矩阵,上三角矩阵是指下三角区所有元素均为同一常量,下三角矩阵是指上三角区所有元素均为同一常量
上三角矩阵 下三角矩阵3.带状矩阵(三对角矩阵)
带状矩阵指所有非零元素都集中在以主对角线为中心的3条对角线的区域,其他区域均为零的矩阵特点:当|i-j|>1时,有aᵢ,ⱼ=0 或者说主对角线的相邻的上下左右元素可以是非0元素,再往外肯定是0元素的矩阵
如下图所示:
带状矩阵4.稀疏矩阵
在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵,但是对于这个“远远多于”并没有准确的定量规定。
比如
稀疏矩阵的例子对于一个二维数组,外观上看是一个矩阵,但是电脑内存是线性存储的,所以电脑会自动按照一定的规则,去把二维数组转换为地址连续的块放到内存中,这些规则分为行优先和列优先
比如下面这个矩阵
1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
如果按行优先,放到内存就是1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
列优先就是1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16
我们在矩阵的压缩存储中,通常的策略就是把二维矩阵通过一定的下标(i,j)的映射规则放入到一维数组中,比如说呢,下面这个对称矩阵
2 | 4 |
4 | 2 |
放到一维数组a中只需包含 2 4,分别为a[0]和a[1],数组的下标0与1与矩阵Ai,j的元素下标i和j的关系就是所谓的映射,这也是定量计算的重点!
设有对称矩阵A[i][j]如下图:(aᵢ,ⱼ=aⱼ,ᵢ )
存储策略:只存储主对角线+下三角区(或主对角线+上三角区),以主对角线+下三角区为例,按照行优先把这些元素放入到一维数组中,就得到了下面的样子的一维数组:
a₁₁ | a₂₁ | a₂₂ | a₃₁ | ... | aₙ,ₙ₋₂ | aₙ,ₙ₋₁ | aₙ,ₙ |
这个一维数组的大小是1+2+3+...+n=n*n(n+1)/2
下面来把行号和列号转换为数组下标:
aᵢ,ⱼ是第i行的元素,所以前面还有i-1行,元素总数为1+2+...+i-1=i*(i-1)/2
j代表其属于第j列,所以最终确定按照行优先的存储aᵢ,ⱼ应该在一维数组的第i*(i-1)/2+j个位置,但是由于数组是从0开始存的,所以对应下标为i*(i-1)/2+j-1
上述讨论的均为在下三角区的情形,此时i>=j
现在根据性质aᵢ,ⱼ=aⱼ,ᵢ可以得到矩阵所有元素与一维数组下标k的对应关系如下:
给出下面的下三角矩阵A[i][j]
存储策略:按照行优先将下三角区域(橙色部分)放入一位数组中,并将常数c放到最后一个位置
得到下图的一位数组
a₁₁ | a₂₁ | a₂₂ | a₃₁ | ... | aₙ,ₙ₋₁ | aₙ,ₙ | c |
这个一位数组的大小为:1+2+3+...+n+1=n*n(n+1)/2+1
这个映射关系和对称矩阵几乎一样,唯一不同的是当i 再来看上三角矩阵 给出下面的上三角矩阵A[i][j] 可以按照行优先原则,把上三角区域的元素(绿色区域),放入一位数组中,并在最后一个位置放入常数c,即可得到下面的一维数组 这个的数组大小与下三角的一样,对于一个元素aᵢ,ⱼ,第i行的元素要存n-i+1个元素,第i-1行要存n-i+2个元素,所以前i-1行的元素总数为n+n-1+...+n-i+2,由于在第j列,所以对应第i行的第j-i+1个元素,总的来说,就是数组中的第n+n-1+...+n-i+2+j-i+1个元素,在数组中下标从0开始所以为n+n-1+...+n-i+2+j-i 综上,可以得到如下的映射关系 给出下面的带状矩阵A[i][j] 存储策略,按照行优先的原则,只存储带状部分,便可以得到下面的一维数组: 先计算一维数组的大小,每行三个元素,共n行,3*n,但第一行和最后一行仅仅2个元素,所以总共个数为3*n-2 下面研究映射关系: 当|i-j|>1时,值就是0 当当|i-j|<=1时,对于一个元素aᵢ,ⱼ,前i-1行,每行有三个,第一行少了一个,总共为3*(i-1)-1个元素,而且找规律可以知道,这个元素在第i行是第j-i+2个元素,所以总共是数组的第3*(i-1)-1+j-i+2个元素即第2*i+j-2个元素,对应数组下标为2*i+j-3 所以映射关系为 下面再来探讨一个逆问题,就是如果给出数组下标k,想要得到这个元素在第几行第几列怎么办呢? 首先,这个元素是第k+1个元素,假设它在第i行第j列,那么前i-1行元素总数为3*(i-1)-1,前i行元素总数为3*i-1,满足下面的不等式 3*(i-1)-1 解得:i>=(k+2)/3 为了让这个i“刚好大于(k+2)/3”,让其向上取整,即i=⌈(k+2)/3⌉(提示:⌈⌉是向上取整,⌊⌋是向下取整) 然后再根据我们上面得出的关系式:k=2*i+j-3综合可得j的值,从而得到i和j即为元素行数和列数 给出两个方法去存储它 第一个方式是顺序存储,通过一个包含行号、列号、元素值的三元组,去进行存储,比如这个矩阵,我们可以用三元组表示为: 可以定义包括i j v 的结构体数组 第二个方法是链式存储的十字链表法 可以表示为下图: 对于每一个非0元素的结点,包含3个数据域行数i、列数j、元素值v,和两个指针域,左边的指针指向同列的下一个元素,右边的指针指向同行的下一个元素 下面给出十字链表的c语言实现代码以及效果图,如果代码有不足之处,可以指出!: 这是效果图: 今天的文章就到这里了(`・ω・´)
a₁₁
a₁₂
a₁₃
...
a₁,ₙ
...
aₙ,ₙ
c
3.带状矩阵
a₁₁
a₁₂
a₂₁
a₂₂
...
aₙ₋₁,ₙ
aₙ,ₙ₋₁
aₙ,ₙ
4.稀疏矩阵
行数 i
列数 j
值 v
1
3
4
1
6
5
2
2
3
2
4
9
3
5
7
4
2
2
#include