【数据结构】为了节省空间,对于特殊矩阵我们可以这样做……

特殊矩阵的压缩存储

  • 导读
  • 一、数组与矩阵
    • 1.1 数组
    • 1.2 数组与线性表
    • 1.3 数组的存储结构
    • 1.4 矩阵在数组中的存储
      • 1.4.1 行优先存储
      • 1.4.2 列优先存储
  • 二、特殊矩阵及其压缩存储
  • 三、对称矩阵及其存储
    • 3.1 方阵与对称矩阵
    • 3.2 对称矩阵的存储
    • 3.3 压缩存储的手动实现
      • 3.3.1 行优先存储
      • 3.3.2 列优先存储
      • 3.3.3 易错点
  • 四、三角矩阵及其存储
    • 4.1 三角矩阵
    • 4.2 三角矩阵的存储
  • 五、三对角矩阵及其存储
    • 5.1 对角矩阵
    • 5.2 三对角矩阵
    • 5.3 三对角矩阵的存储
      • 5.3.1 行优先存储
      • 5.3.2 列优先存储
      • 5.3.3 思维拓展
  • 六、稀疏矩阵及其存储
    • 6.1 稀疏矩阵
    • 6.2 稀疏矩阵的存储
      • 6.2.1 三元组存储
      • 6.2.2 十字链表法
  • 结语

封面

导读

大家好,很高兴又和大家见面啦!!!

数组相信大家都不陌生了,在C语言中我们就开始接触数组相关的知识点了,在学习指针时,我们进一步探讨了数组和指针的关系,等到我们开始学习线性表时我们才知道,原来数组这种存储方式为线性存储,并且它本身就是一种数据结构——顺序表。
经过这么长时间的学习,不得不说数组在计算机的知识体系中,还是处于一个比较重要的位置的。

今天的内容中,我们同样是要学习数组。和前面的学习不同,今天我们要学习的内容是如何将一个矩阵中的数据存放进数组。在今天的内容中,我们会详细的探讨一下数组和矩阵之间的关系。

一、数组与矩阵

在介绍数组和矩阵前,我们需要先复习一下数组的相关知识点;

1.1 数组

数组是由n(n>=1)个相同类型的数据元素构成的有限序列,每个数据元素称为一个数组元素,每个元素在n个线性关系中的序号称为该元素的下标,下标的取值范围称为数组的维界

在之前的学习中,我们只知道数组有三要素:数组名、数组大小以及数组元素的数据类型。现在我们又新学了一个概念,那就是数组下标的取值范围的专业术语是维界。这些知识点因为都是大家比较熟悉的,我就不再展开赘述;

1.2 数组与线性表

在学习线性表时,我们介绍的第一种线性表就是顺序表,当时我们有提到过数组就是用来描述线性表的顺序存储结构的。因此数组可以看做是线性表的一种推广。

在学习数组时我们有学过,数组是有一维数组、二维数组甚至是多维数组的,而一维数组我们就可以将其视为是一个线性表,二维数组则可以看做其元素是定长线性表的一种线性表,以此类推。

数组这种结构在创建时是需要在内存中申请一块连续的存储空间的,因此它一旦被定义,其维数和维界则不可再被修改。因此,对于数组而言,除了进行初始化和销毁外,数组只会有存取元素和修改元素的操作。

1.3 数组的存储结构

数组中的元素在存储时在内存中是进行从低地址到高地址的顺序进行连续存放的,因此在知道每个元素所占空间大小后,我们就可以通过数组首元素地址与第k个元素的下标来获取该元素在内存空间中的地址。

假设首元素地址为LOC(arr[0]),数组元素所占空间大小为sizeof(Elemtype),下面我们分别来看一下一维数组和二维数组在内存中的存储以及如何获取数组中的各个元素的地址;

  • 一维数组arr[m]在内存中的存储如下所示:
    一维数组在内存中的存储

如果用L来表示元素类型所占空间大小,则存储结构关系式可以写成:
L O C ( a r r [ i ] ) = L O C ( a r r [ 0 ] ) + i × L ( 0 < = i < m ) LOC(arr[i]) = LOC(arr[0]) + i × L(0<=i<m) LOC(arr[i])=LOC(arr[0])+i×L(0<=i<m)

  • 二维数组arr[m][n]在内存中的存储如下所示:

二维数组在内存中的存储

在这个二维数组中,它是由m个一维数组组成,在每个一维数组中都有n个元素,二维数组中的元素的地址之间的关系为:

  • 同一个一维数组中相邻两个元素之间的地址相差1 × sizeof(Elemtype)
  • 每个一维数组从首元素到尾元素之间的地址相差(n - 1) × sizeof(Elemtype)
  • 两个相邻的数组之间的地址相差n × sizeof(Elemtype)

对于第arr[i][j]这个元素而言,我们在计算它的地址时的思路应该是:

  1. 先计算一维数组之间的地址差值:下标为i这个一维数组arr[i],它与二维数组中的首元素arr[0]这个数组之间的地址相差(i × n) × sizeof(Elemtype)
  2. 再计算一维数组中元素之间的地址差值:在arr[i]这个一维数组中,元素arr[i][j]与首元素arr[i][0]之间又相差j × sizeof(Elemtype)个地址;
  3. 最后计算该元素的地址:arr[i][j]这个元素在内存中的地址为 首元素地址 + 一维数组之间的地址差值 + 一维数组中元素之间的地址差值 首元素地址 + 一维数组之间的地址差值 + 一维数组中元素之间的地址差值 首元素地址+一维数组之间的地址差值+一维数组中元素之间的地址差值也就是LOC(arr[0]) + (i × n) × sizeof(Elemtype) + j × sizeof(Elemtype),最后合并同类项可以得到LOC(arr[0]) + (i × n + j) × sizeof(Elemtype)

如果用L来表示元素类型所占空间大小,则存储结构关系式可以写成:
L O C ( a r r [ i ] [ j ] ) = L O C ( a r r [ 0 ] ) + ( i × n + j ) × L ( 0 < = i < m , 0 < = j < n ) LOC(arr[i][j]) = LOC(arr[0]) + (i × n + j) × L(0<=i<m,0<=j<n) LOC(arr[i][j])=LOC(arr[0])+(i×n+j)×L(0<=i<m0<=j<n)

1.4 矩阵在数组中的存储

m × n m×n m×n个数 a i j ( i = 1 , 2 , 3 , … … , m ; j = 1 , 2 , 3 , … … , n ) a_{ij}(i=1,2,3,……,m;j=1,2,3,……,n) aij(i=1,2,3,……,m;j=1,2,3,……,n)排列成的m行n列的矩形表格称为m×n矩阵,简记为A或 ( a i j ) m × n ( i = 1 , 2 , 3 , … … , m ; j = 1 , 2 , 3 , … … , n ) (a_{ij})_{m×n}(i=1,2,3,……,m;j=1,2,3,……,n) (aij)m×n(i=1,2,3,……,m;j=1,2,3,……,n),当 m = n m=n m=n时,称A为n阶方阵。

在二维数组中,由于它是数组元素为定长线性表的线性表,在逻辑上我们同样也可以将其看做是一个矩阵。比如一个二维数组arr[m][n]我们在逻辑上就可以将其看做是一个 m × n m×n m×n的矩阵,如下图所示:
二维数组的逻辑结构
如果我们要将m×n的矩阵中的所有元素存入到这样一个二维数组的话,那我们就有两种存储方式:

  • 按照行优先进行存储:以先行后列的存储方式,将行号较小的元素优先进行存储,行号相同则将列号较小的元素进行优先存储;
  • 按照列优先进行存储:以先列后行的存储方式,将列号较小的元素优先进行存储,列号相同则将行号较小的元素进行优先存储;

这两种存储方式在逻辑上和实际内存中的存储上都是有所差异的,下面我们就来分别看一下这两种存储方式;

1.4.1 行优先存储

当我们将m×n的矩阵A通过行优先将矩阵中的各个元素放入二维数组arr[m][n]时,矩阵中的元素 a i j ( i = 1 , 2 , 3 , … … , m ; j = 1 , 2 , 3 , … … , n ) a_{ij}(i=1,2,3,……,m;j=1,2,3,……,n) aij(i=1,2,3,……,m;j=1,2,3,……,n)所对应的数组元素就为arr[i-1][j-1]。可以看到矩阵元素的下标与所对应的数组元素的下标相差1,这是因为在矩阵中,下标是从1开始计数,而在数组中下标是从0开始计数。
如果用L来表示数组中元素所占空间大小,则矩阵中的元素 a i j ( i = 1 , 2 , 3 , … … , m ; j = 1 , 2 , 3 , … … , n ) a_{ij}(i=1,2,3,……,m;j=1,2,3,……,n) aij(i=1,2,3,……,m;j=1,2,3,……,n)在内存中的地址为: L O C ( a i j ) = L O C ( a r r [ 0 ] ) + [ ( i − 1 ) × n + ( j − 1 ) ] × L LOC(a_{ij}) = LOC(arr[0]) + [(i - 1) × n + (j - 1)] × L LOC(aij)=LOC(arr[0])+[(i1)×n+(j1)]×L
矩阵元素在内存中的存储形式如下所示:

行优先存储

1.4.2 列优先存储

相比于行优先,我们在进行列优先存储时,我们可以把m×n的矩阵A看做是一个n×m的矩阵B,此时我们如果将其存放进一个与之对应的二维数组arr2[n][m]的话,实际上我们也是在进行行优先存储,只不过相比于矩阵A此时的矩阵B的行号就是A的列号。矩阵B中的元素 b j i ( j = 1 , 2 , 3 , … … , n ; i = 1 , 2 , 3 , … … , m ) b_{ji}(j=1,2,3,……,n;i=1,2,3,……,m) bji(j=1,2,3,……,n;i=1,2,3,……,m)所对应的数组元素为arr2[j-1][i-1]
如果用L来表示数组中元素所占空间大小,则矩阵中的元素 b j i ( j = 1 , 2 , 3 , … … , n ; i = 1 , 2 , 3 , … … , m ) b_{ji}(j=1,2,3,……,n;i=1,2,3,……,m) bji(j=1,2,3,……,n;i=1,2,3,……,m)在内存中的地址为: L O C ( a i j ) = L O C ( a r r [ 0 ] ) + [ ( j − 1 ) × m + ( i − 1 ) ] × L LOC(a_{ij}) = LOC(arr[0]) + [(j - 1) × m + (i - 1)] × L LOC(aij)=LOC(arr[0])+[(j1)×m+(i1)]×L
矩阵元素在内存中的存储形式如下所示:

列优先存储

二、特殊矩阵及其压缩存储

前面的介绍主要是说明普通的m×n的矩阵在内存中的一个存储方式。当我们需要存储普通矩阵的数据时,我们就可以借助与其元素个数相同的二维矩阵来进行相应的存储,根据自己的具体需求来选择行优先还是列优先的存储方式。

但是当我们在存储矩阵时,并不是矩阵中的所有数据都需要进行存储。对于一些特殊的矩阵,我们在进行存储时,可以采用更高效的存储方式——压缩存储。

  • 特殊矩阵

特殊矩阵指的是具有许多相同矩阵元素或零元素,并且这些相同矩阵元素或零元素的分布有一定规律性的矩阵。常见的特殊矩阵有对称矩阵上(下)三角矩阵对角矩阵稀疏矩阵等。

  • 压缩存储

压缩存储指的是为多个值相同的元素只分配一个存储空间,对零元素不分配存储空间。其目的是为了节省空间。

  • 特殊矩阵的压缩存储方法

在存储特殊矩阵时,我们可以通过找出特殊矩阵中值相同的矩阵元素的分布规律,把那些呈现规律性分布的、值相同的多个矩阵元素压缩存储到一个存储空间中。

了解了什么是特殊矩阵和压缩存储后,下面我们就来看一下几种常见的特殊矩阵及其存储方式;

三、对称矩阵及其存储

3.1 方阵与对称矩阵

在n阶方阵中,元素可以划分为3个部分:上三角区、主对角线和下三角区,如下所示:

n阶方阵的区域划分

对一个n阶方阵A中的任意一个元素 a i j a_{ij} aij都有 a i j = a j i ( 1 < = i , j < = n ) a_{ij}=a_{ji}(1<=i,j<=n) aij=aji(1<=i,j<=n),则称其为对称矩阵。

3.2 对称矩阵的存储

在n阶对称矩阵中,因为上三角区和下三角区对应的元素都相同,若仍采用二维数组来存放矩阵中的元素,则会浪费几乎一半的空间。为了减少对空间的消耗,我们可以将n阶对称矩阵A存放在一个一维数组B[n(n+1)/2]中,即元素 A i j A_{ij} Aij存放在B[k]中。比如只存放主对角线和下三角区的元素。

那我们这样存放后如何来查找上三角区对应的元素呢?

对于上三角区各个元素的查找,这里我们就可以利用对称矩阵的特性——n阶方阵A中的任意一个元素 a i j a_{ij} aij都有 a i j = a j i ( 1 < = i , j < = n ) a_{ij}=a_{ji}(1<=i,j<=n) aij=aji(1<=i,j<=n)来进行查找。
也就是我在数组中存放的是 A 10 A_{10} A10那我要查找 A 01 A_{01} A01的话我只需要访问存放 A 10 A_{10} A10的数组元素B[k]即可。

这就是对称矩阵的压缩存储——将两个值相同的元素存入通一个空间内,将原先需要 n 2 n^2 n2的空间大小压缩成 n ( n + 1 ) / 2 n(n+1)/2 n(n+1)/2的空间大小。

相比与使用二维数组存放的方式,这种压缩存储方式,可以让我们节省整个下/上三角区的空间大小,也就是 n ( n − 1 ) / 2 n(n-1)/2 n(n1)/2的空间大小。

3.3 压缩存储的手动实现

当我们在实现压缩存储时,我们会面临两个问题:

  1. 矩阵中的元素如何进行存放
  2. 如何在一维数组B[n(n+1)/2]中找到矩阵元素 A i j A_{ij} Aij

在前面我们介绍了两种矩阵的存放方式——行优先存储和列优先存储。

对于不同类型的矩阵在二维数组中进行行优先和列优先的存储会有些许的不同:

  • m×n的矩阵 A A A在进行行优先和列优先存储时有两点不同:
    • 二维数组的不同——进行行优先存储时,对应的二维数组为B[m][n];而在进行列优先存储时,对应的二维数组为B[n][m]
    • 矩阵元素在二维数组中存放的位置不同——进行行优先存储时,数组元素B[0][1]存放的是矩阵元素 A 01 A_{01} A01;而进行列优先存储时,数组元素B[0][1]存放的是矩阵元素 A 10 A_{10} A10
  • 对于n阶方阵 A A A而言,两种存储方式需要的二维数组都是B[n][n],但是还是有一点不同:
    • 矩阵元素在二维数组中存放的位置不同——进行行优先存储时,数组元素B[0][1]存放的是矩阵元素 A 01 A_{01} A01;而进行列优先存储时,数组元素B[0][1]存放的是矩阵元素 A 10 A_{10} A10
  • 对于n阶对称矩阵 A A A而言,在二维数组B[n][n]中不管是哪一种存储方式,存储的结果都是相同的;

当我们对n阶对称矩阵进行压缩存储时,这里以下三角区和主对角线元素为例,实际需要存储的元素如下图所示:

对称矩阵的压缩存储
要进行压缩存储的元素从 a 11 a_{11} a11开始,到 a n n a_{nn} ann结束,在矩阵中的排列就是一个直角三角形。

从行来看这个三角形的话我们不难发现,每一行的元素个数与对应行号相等:
第一行有一个元素;
第二行有两个元素;
……
第n行有n个元素;

而从列来看的话每一列的元素个数与对应的列号刚好相反:
第一行有n个元素;
第二行有n-1个元素;
……
第n行有1个元素;

不管是行也好,还是列也好,相邻两行或者相邻两列之间的元素个数相差1,根据等差数列的求和公式我们可以很容易的算出,我们实际需要存储的元素有 n × ( n + 1 ) / 2 n×(n+1)/2 n×(n+1)/2个元素,如果想要存放这些元素,那么我们所需要的一维数组就应该是 Elemtype arr[n(n+1)/2],这里为了方便说明,我们就使用数组B[n(n+1)/2]来说明。

可见在这种情况下行优先与列优先的存储方式的选择就决定了矩阵元素 A i j A_{ij} Aij在一维数组B[n(n+1)/2]中存放的位置。下面我们就来分别探讨矩阵元素 A i j A_{ij} Aij在进行不同的存储方式时与所对应的数组元素B[k]之间的关系;

3.3.1 行优先存储

如果采用行优先的方式来存放这些元素,那元素在内存中的位置如下所示:

行优先存储
从上图中我们可以看到,按照行优先存储时,每行之间都是顺序存储的,每一行的元素之间也都是顺序存储的。在数组中,数组下标表示的是当前元素之前的元素个数,根据数组下标的这个特性,我们不难得出一个结论——我们在寻找矩阵元素 A i j A_{ij} Aij所对应的数组元素,实际上就是在计算该元素之前的元素个数。

如何计算元素个数相信是大家比较关心的一个问题,为了方便大家更好的理解,我们可以把每一行的元素个数给列出来,如下所示:

行号元素个数
11
22
33
…………
i-1i-1
ii
i+1i+1
…………
n-2n-2
n-1n-1
nn

从上表中我们不难得出一个结论——1到n行的元素个数刚好是1到n的一个公差为1的等差数列。

因此,当我们想计算第 i i i 行的元素总个数时,我们只需要将从第 1 1 1 行到第 i i i 行的所有元素个数都给加起来。第 1 1 1 行到第 i i i 行的元素个数总和我们可以通过等差数列求和公式得到: S i = ( 1 + i ) × i / 2 S_i=(1+i)×i/2 Si=(1+i)×i/2

对于同一行而言,它的每一列只存在一个元素,因此从 1 1 1 列到第j列共有 j j j 个元素。也就是: S j = j S_j=j Sj=j

当我们要计算第 i + 1 i+1 i+1行的第 j j j 列元素的元素个数时,实际上就是计算从第 1 1 1 行到第i行的元素总个数 S i S_i Si以及第 i + 1 i+1 i+1行的第 j j j 列个元素 S j S_j Sj的总和,那我们就不难得到从元素 A 11 A_{11} A11到元素 A ( i + 1 ) j A_{(i+1)j} A(i+1)j的元素总个数: S ( i + 1 ) j = S i + S j = ( 1 + i ) × i / 2 + j S_{(i+1)j}=S_i+S_j=(1+i)×i/2+j S(i+1)j=Si+Sj=(1+i)×i/2+j

而对于数组而言,由于数组下标是记录的该元素之前的元素个数,那也就是说我想要计算 A ( i + 1 ) j A_{(i+1)j} A(i+1)j的数组下标,实际上也就是计算从元素 A 11 A_{11} A11到元素 A ( i + 1 ) ( j − 1 ) A_{(i+1)(j-1)} A(i+1)(j1)的元素总个数。因此我们可以很容易的得到其数组下标k的值: k = S ( i + 1 ) ( j − 1 ) = ( 1 + i ) × i / 2 + j − 1 k=S_{(i+1)(j-1)}=(1+i)×i/2+j-1 k=S(i+1)(j1)=(1+i)×i/2+j1

当我们要查找元素 A i j A_{ij} Aij所对应的数组下标k,实际上就是在计算从第 1 1 1 行到第 i − 1 i-1 i1行的元素总个数 S i − 1 S_{i-1} Si1和第 i i i 行第 j − 1 j-1 j1列的元素总个数 S j S_j Sj的和 S ( i − 1 ) ( j − 1 ) S_{(i-1)(j-1)} S(i1)(j1)。通过前面推导出来的公式,我们不难得到其对应的数组下标k的值: k = S ( i − 1 ) ( j − 1 ) = i × ( i − 1 ) / 2 + j − 1 k=S_{(i-1)(j-1)}=i×(i-1)/2+j-1 k=S(i1)(j1)=i×(i1)/2+j1

3.3.2 列优先存储

如果采用列优先的存储方式,那元素在内存中的位置如下所示:

列优先存储
在列优先存储中,由于下三角区的元素个数与列号并不是一一对应的,因此我们在计算元素总数时我们需要计算每列的元素个数以及总列数,如下表所示:

列号元素个数
1n
2n-1
3n-2
…………
j-1n-j+2
jn-j+1
j+1n-j
…………
n-23
n-12
n1

从表中不难看出列优先存储在列数上是一个公差为1的递增的等差数列,而每列的元素个数则是一个公差为1的递减的等差数列。根据等差数列的求和公式 (首项 + 尾项) × 项数 ÷ 2 (首项+尾项)×项数÷2 (首项+尾项)×项数÷2可知,公式中的首项为第 1 1 1 列的元素个数,也就是 n n n;项数为对应的列号,比如要求 j j j 列的元素总和,那对应的项数就为 j j j ;尾项则是第j列的元素个数 n − j + 1 n-j+1 nj+1

因此从第 1 1 1 列到第 j j j 列的元素总个数 S j S_j Sj为:
S j = ( n + n − j + 1 ) × j / 2 = ( 2 n − j + 1 ) j / 2 S_j=(n+n-j+1)×j/2=(2n-j+1)j/2 Sj=(n+nj+1)×j/2=(2nj+1)j/2

由于每一列的元素个数是一个递减的等差数列,因此,对于不同列来说,它们在同一行所有的元素个数是不相同的,如下所示:

列号从第1行到第i行的元素个数
1i
2i-1
3i-2
…………
j-1i-j+2
ji-j+1
j+1i-j
…………
n-2i-n+3
n-1i-n+2
ni-n+1

因此,第 j j j 列的第 i i i 行元素所对应的元素总个数 S i S_i Si为:
S i = i − j + 1 S_i=i-j+1 Si=ij+1

那么对于下三角区和主对角线的这些元素通过列优先进行存储时,从 a 11 a_{11} a11 a i j a_{ij} aij所对应的元素总个数 S i j S_{ij} Sij实际上是从第 1 1 1 列到 j − 1 j-1 j1列的元素总个数加上第 j j j列第 i i i 行的元素总个数:
S i j = S j − 1 + S i = ( 2 n − j + 2 ) ( j − 1 ) / 2 + i − j + 1 S_{ij}=S_{j-1}+S_i=(2n-j+2)(j-1)/2+i-j+1 Sij=Sj1+Si=(2nj+2)(j1)/2+ij+1

在数组中,由于数组下标比元素总个数少1,因此元素 a i j a_{ij} aij所对应的数组下标k为:
k = S i j − 1 = ( 2 n − j + 2 ) ( j − 1 ) / 2 + i − j + 1 − 1 = ( 2 n − j + 2 ) ( j − 1 ) / 2 + i − j k=S_{ij}-1=(2n-j+2)(j-1)/2+i-j+1-1=(2n-j+2)(j-1)/2+i-j k=Sij1=(2nj+2)(j1)/2+ij+11=(2nj+2)(j1)/2+ij

3.3.3 易错点

  1. 矩阵元素下标的取值范围

这里我们需要注意的是前面我们介绍的不管是行优先存储还是列优先存储对于n阶对称矩阵A而言,其所对应的元素 A i j A_{ij} Aij下标 i 、 j i、j ij 的取值范围是 ( 1 < = i , j < = n ) (1<=i,j<=n) (1<=i,j<=n);而对于数组B[n(n+1)/2]而言,其数组元素B[k]所对应的数组下标 k k k 的取值范围是 ( 0 < = k < = n ( n + 1 ) / 2 − 1 ) (0<=k<=n(n+1)/2-1) (0<=k<=n(n+1)/21)

可以看到在数组中元素总个数与下标之间的差值为1,因此我们在将矩阵元素下标转换为所对应的数组元素下标时,所求得的元素总数 S i j S_{ij} Sij − 1 -1 1后得到的结果才是对应的数组元素下标k。

但是并不是所有遇到的矩阵下标的取值范围都是从1开始的,比如有的n阶对称矩阵的下标取值范围是 ( 0 < = i , j < = n − 1 ) (0<=i,j<=n-1) (0<=i,j<=n1),在这种情况下矩阵元素下标与对应的数组元素下标是一一没有差值了,此时算出来的 a i j a_{ij} aij的元素个数就是其所对应的数组下标。

因此数组下标与矩阵元素对应的下标之间的关系需要根据矩阵元素下标的取值范围来进行确定

  1. 存储区域

在前面的介绍中,我们都是以下三角区和主对角线为例子来探讨在不同的存储形式中矩阵元素下标与数组元素下标之间的关系。因此我们得到的结论如下所示:

在n阶对称矩阵A中,其矩阵元素 A i j A_{ij} Aij下标 i 、 j i、j ij 的取值范围是 ( 1 < = i , j < = n ) (1<=i,j<=n) (1<=i,j<=n)
存放矩阵元素的数组B[n(n+1)/2],其数组元素B[k]所对应的数组下标 k k k 的取值范围是
( 0 < = k < = n ( n + 1 ) / 2 − 1 ) (0<=k<=n(n+1)/2-1) (0<=k<=n(n+1)/21)
通过行优先存储时,矩阵元素下标与数组元素下标之间的关系为:
k = S ( i − 1 ) ( j − 1 ) = i × ( i − 1 ) / 2 + j − 1 k=S_{(i-1)(j-1)}=i×(i-1)/2+j-1 k=S(i1)(j1)=i×(i1)/2+j1
通过列优先存储时,矩阵元素下标与数组元素下标之间的关系为:
k = S i j − 1 = ( 2 n − j + 2 ) ( j − 1 ) / 2 + i − j + 1 − 1 = ( 2 n − j + 2 ) ( j − 1 ) / 2 + i − j k=S_{ij}-1=(2n-j+2)(j-1)/2+i-j+1-1=(2n-j+2)(j-1)/2+i-j k=Sij1=(2nj+2)(j1)/2+ij+11=(2nj+2)(j1)/2+ij

但是,当我们存储的矩阵区域为主对角线与上三角区时,我们不难发现,此时的矩阵元素下标与下三角区的元素下标刚好相反,为 A j i A_{ji} Aji,因此矩阵元素下标与数组下标之间的关系式也是稍有差异的,这里我就不多加赘述了。

四、三角矩阵及其存储

介绍完了n阶对称矩阵的压缩存储,下面我们再来看一下什么是三角矩阵以及其对应的压缩存储。

4.1 三角矩阵

三角矩阵和对称矩阵一样,也是一种特殊的n阶方阵。在三角矩阵中,矩阵的下三角区和上三角区这两块区域中其中一块区域都为通一个常量,这里我们还是以下三角区和主对角线为例,如下所示:
三角矩阵

向这种只有主对角线与下/上三角区中存放元素,上/下三角区中存放同一常量的特殊n阶方阵,我们将其称为三角矩阵。

4.2 三角矩阵的存储

在三角矩阵中,如果我们以二维数组来存放矩阵中的所有元素,对于存放常量的区域来说,其实也是一种空间的浪费。

按照压缩存储的思想,我们在存放三角矩阵中的元素时,我们实际上可以和对称矩阵一样通过一维数组存放下/上三角区加上主对角线的全部元素,再通过额外的一个空间来存放上/下三角区中存放的常量。

因此,相比于对称矩阵而言,三角矩阵在存储时所使用的数组为B[n(n+1)/2+1],这多出来的一个区域是用来存放常量的区域。

在对称矩阵中我们已经很好的分析了如何对下三角区和主对角线的矩阵元素进行存储,这里我就不再展开赘述。下面我们直接给结论,这里以下三角区和主对角线为例,上三角区存放同一常量:

在n阶三角矩阵A中,其矩阵元素 A i j A_{ij} Aij下标 i 、 j i、j ij 的取值范围是 ( 1 < = j < = i < = n ) (1<=j<=i<=n) (1<=j<=i<=n)
存放常量的矩阵元素 A i j A_{ij} Aij下标 i 、 j i、j ij 的取值范围是 ( 1 < = i < j < = n ) (1<=i<j<=n) (1<=i<j<=n)
存放矩阵元素的数组B[n(n+1)/2+1],其数组元素B[k]所对应的数组下标 k k k 的取值范围是
( 0 < = k < = n ( n + 1 ) / 2 ) (0<=k<=n(n+1)/2) (0<=k<=n(n+1)/2)
存放常量的数组元素B[k]所对应的数组下标 k k k为:
k = n ( n + 1 ) / 2 k=n(n+1)/2 k=n(n+1)/2
通过行优先存储时,矩阵元素下标与数组元素下标之间的关系为:
k = S ( i − 1 ) ( j − 1 ) = i × ( i − 1 ) / 2 + j − 1 k=S_{(i-1)(j-1)}=i×(i-1)/2+j-1 k=S(i1)(j1)=i×(i1)/2+j1
通过列优先存储时,矩阵元素下标与数组元素下标之间的关系为:
k = S i j − 1 = ( 2 n − j + 2 ) ( j − 1 ) / 2 + i − j + 1 − 1 = ( 2 n − j + 2 ) ( j − 1 ) / 2 + i − j k=S_{ij}-1=(2n-j+2)(j-1)/2+i-j+1-1=(2n-j+2)(j-1)/2+i-j k=Sij1=(2nj+2)(j1)/2+ij+11=(2nj+2)(j1)/2+ij

五、三对角矩阵及其存储

介绍完了对称矩阵和三角矩阵,接下来我们来看另一种特殊的n阶方阵——三对角矩阵。

5.1 对角矩阵

对角矩阵时一个除主对角线之外的其它元素全为0的矩阵。对角线上的元素可以为0或者其他值,如下所示:
对角矩阵
对角线上元素相等的对角矩阵称为数量矩阵对角线上元素全为1的对角矩阵称为单位矩阵对角矩阵也称为带状矩阵

5.2 三对角矩阵

在n阶方阵A中,当任意一个元素 A i j A_{ij} Aij的下标满足 ∣ i − j ∣ > 1 |i-j|>1 ij>1时,有 A i j = 0 ( 1 < = i , j < = n ) A_{ij}=0(1<=i,j<=n) Aij=0(1<=i,j<=n),则该n阶方阵称为三对角矩阵,如下所示:

三对角矩阵

不难看出,除了主对角线以及左右两相邻的对角线上的元素外,其他区域都为0,因此三对角矩阵也是可以采用压缩存储的。

5.3 三对角矩阵的存储

对于三对角矩阵而言,根据压缩存储的思想,对于值为0的元素不分配存储空间,因此我们主要存储的就是主对角线和左右两相邻对角线上的元素。

在n阶方阵中,三对角矩阵除了第1行和第n行只有两个元素外,其余行都有3个元素,因此我们很容易得到需要存储的元素个数为 3 n − 2 3n-2 3n2

三对角矩阵在存储时,我们同样可以选择行优先存储和列优先存储两种方式。

5.3.1 行优先存储

当我们通过行优先来存储n阶三对角矩阵A时,矩阵中的元素 A i j ( 1 < = i , j < = n ) A_{ij}(1<=i,j<=n) Aij(1<=i,j<=n)与存储矩阵元素的数组B[3n-2]所对应的数组元素B[k] 0 < = k < = 3 n − 3 0<=k<=3n-3 0<=k<=3n3之间的关系如下所示:
行优先存储

在计算从第 1 1 1 行到第 i i i 行的元素个数时,我们以主对角线的元素为起点与终点,这样我们能够很容易的得到元素的总个数为 3 i − 2 3i-2 3i2,对应的数组下标则是 k = 3 i − 3 k=3i-3 k=3i3
通过主对角线上的元素来推算左右两侧的元素,我们不难得到

  • 位于主对角线左侧的元素总个数为 3 i − 3 3i-3 3i3,对应的数组下标则是 k = 3 i − 4 k=3i-4 k=3i4
  • 位于主对角线右侧的元素总个数为 3 i − 1 3i-1 3i1,对应的数组下标则是 k = 3 i − 2 k=3i-2 k=3i2

5.3.2 列优先存储

当我们通过列优先来存储n阶三对角矩阵A时,矩阵中的元素 A i j ( 1 < = i , j < = n ) A_{ij}(1<=i,j<=n) Aij(1<=i,j<=n)与存储矩阵元素的数组B[3n-2]所对应的数组元素B[k] 0 < = k < = 3 n − 3 0<=k<=3n-3 0<=k<=3n3之间的关系如下所示:
列优先存储

通过观察我们不难得到,对于三对角矩阵不管是行优先还是列优先,我们在计算矩阵元素对应的数组下标时,其实没有区别。从函数的角度来看行优先以行号 i i i 为自变量,数组下标 k k k 为因变量,列优先以列号 j j j 为自变量,数组下标 k k k 为因变量,它们所对应的法则是一致的。对于函数而言两个行数的定义域一致,对应法则一致,那么这两个函数就是同一个函数。

因此在计算从第 1 1 1 行到第 j j j 列的元素个数时,我们以主对角线的元素为起点与终点,这样我们能够很容易的得到元素的总个数为 3 j − 2 3j-2 3j2,对应的数组下标则是 k = 3 j − 3 k=3j-3 k=3j3
通过主对角线上的元素来推算左右两侧的元素,我们不难得到

  • 位于主对角线上方的元素总个数为 3 j − 3 3j-3 3j3,对应的数组下标则是 k = 3 j − 4 k=3j-4 k=3j4
  • 位于主对角线下方的元素总个数为 3 j − 1 3j-1 3j1,对应的数组下标则是 k = 3 j − 2 k=3j-2 k=3j2

5.3.3 思维拓展

刚才我们通过一种比较巧妙的方式介绍了在n阶三对角矩阵的行优先存储和列优先存储这两种存储方式中矩阵元素下标 i , j ( 1 < = i , j < = n ) i,j(1<=i,j<=n) i,j(1<=i,j<=n)与数组元素下标 k ( 0 < = k < = 3 n − 3 ) k(0<=k<=3n-3) k(0<=k<=3n3)之间的关系。

现在我们要介绍的一种新的方式——从函数的角度来获取在这两种存储方式下的矩阵元素下标 i , j ( 1 < = i , j < = n ) i,j(1<=i,j<=n) i,j(1<=i,j<=n)与数组元素下标 k ( 0 < = k < = 3 n − 3 ) k(0<=k<=3n-3) k(0<=k<=3n3)之间的关系。大家可能不太理解,没关系,我们一起往下看;

前面我们也提到过在三对角矩阵中,不管是行优先还是列优先,实际上它们都是同一个函数,因此我们不妨假设矩阵元素下标 i , j ( 1 < = i , j < = n ) i,j(1<=i,j<=n) i,j(1<=i,j<=n)与数组元素下标 k ( 0 < = k < = 3 n − 3 ) k(0<=k<=3n-3) k(0<=k<=3n3)之间的关系式为: a i + b j + c = k ( a , b , c 为实数 ) ai+bj+c=k(a,b,c为实数) ai+bj+c=k(a,b,c为实数)函数的定义域为 1 < = i , j < = n 1<=i,j<=n 1<=i,j<=n,函数的值域为 0 < = k < = 3 n − 3 0<=k<=3n-3 0<=k<=3n3

对于这个二元一次方程的求解,我们可以通过赋值法来求出自变量的系数 a , b a,b a,b与常数 c c c ,这里我们以行优先为例,矩阵元素下标与数组元素下标如下所示:

ijk
110
121
212
223
………………
n-1n3n-5
nn-13n-4
nn3n-3

下面我们将前四个元素中任意3个元素的值带入到表达式,不难得到结果: a = 2 , b = 1 , c = − 3 a=2,b=1,c=-3 a=2,b=1,c=3

因此矩阵元素下标与数组元素下标之间的关系式为:
2 i + j − 3 = k ( 1 < = i , j < = n ) 2i+j-3=k(1<=i,j<=n) 2i+j3=k(1<=i,j<=n)

之后我们可以将最后3个元素中的任意一个带入进行验算,可以证明我们求出来的表达式正是矩阵元素下标与数组元素下标之间的关系式。

同样,如果我们以列优先为例来进行求解,我们会得到表达式 2 j + i − 3 = k ( 1 < = i , j < = n ) 2j+i-3=k(1<=i,j<=n) 2j+i3=k(1<=i,j<=n)
表达式的求解过程我就不多加赘述了。

根据三对角矩阵行列下标之间的关系式 ∣ i − j ∣ < = 1 |i-j|<=1 ij<=1,我们以 i − j = 1 i-j=1 ij=1为例,还可以得到行下标与数组元素下标之间的关系式 i = ( k + 1 ) / 3 + 1 , ( 0 < = k < = 3 n − 3 ) i=(k+1)/3+1,(0<=k<=3n-3) i=(k+1)/3+1,(0<=k<=3n3)
表达式结果向下取整。如当 k = 6 k=6 k=6时, i = 10 / 3 ≈ 3 i=10/3≈3 i=10/33

由表达式 2 i + j − 3 = k ( 1 < = i , j < = n ) 2i+j-3=k(1<=i,j<=n) 2i+j3=k(1<=i,j<=n)我们可以得到表达式 j = k − 2 i + 3 j=k-2i+3 j=k2i+3。接下来我们将刚刚求出的 i i i 值与已知的 k k k 值带入,就能得到 j = 3 j=3 j=3。也就是在行优先存储中B[6]所对应的矩阵元素为 A 33 A_{33} A33

对于该结果我们还可以根据前面介绍的行优先存储中矩阵元素行下标与数组元素下标之间的关系式来进行验算。由矩阵元素的下标可知,该元素是位于主对角线上的元素,因此我们可以根据表达式 k = 3 i − 3 ( 1 < = i < = n ) k=3i-3(1<=i<=n) k=3i3(1<=i<=n)求出 k = 3 × 3 − 3 = 6 k=3×3-3=6 k=3×33=6 。可以看到矩阵元素 A 33 A_{33} A33就是数组中下标为6的元素。

六、稀疏矩阵及其存储

在n阶矩阵中还有一类特殊矩阵——稀疏矩阵。下面我们来看看什么是稀疏矩阵;

6.1 稀疏矩阵

在n阶矩阵中,矩阵元素的总个数为 s s s,非零元素个数为 t t t,当 s > > t s>>t s>>t时,我们称该矩阵为稀疏矩阵。如下图所示:
4阶稀疏矩阵
向这种非零元素的个数远小于矩阵元素的个数且元素分布散乱的矩阵我们就可以将其称为稀疏矩阵。稀疏矩阵即可以是n阶方阵也可以是m×n阶矩阵。

6.2 稀疏矩阵的存储

由于稀疏矩阵中的非零元素的分布是散乱的,因此我们无法只对非零元素的值进行存储,为了能够准确的找到元素在矩阵中的位置,我们有两种存储的思路——三元组存储与十字链表存储。

6.2.1 三元组存储

所谓的三元组存储其实就是我们在存储非零元素时将非零元素的行标、列标与元素值一同存储起来。以上图列举的4阶稀疏矩阵为例,我们对非零元素进行三元组存储的方法如下所示:

ijv
121
141
231
311
441

在三元组存储中因为我们是借助行标与列标来对元素进行定位的,因此不管元素的值是否相同,我们都必须分配相应的空间来记录它的行标、列标与元素值。

为了访问三元组中的成员,我们需要借助相应的数据结构来进行存放,比如通过顺序表或者链表。当我们想要访问某一个元素时,不管是顺序表还是链表,我们需要通过对应的行标和列标来进行定位,因此,在三元组存储中我们无法做到随机存取。

6.2.2 十字链表法

对于稀疏矩阵的第二种存储方式十字链表法,我们可以理解为通过两个指针数组分别记录矩阵的行与列,记录行的指针数组的指针指向的是同行对应的下一个非零元素,记录列的指针数组的指针指向的是同列对应的下一个非零元素,如下所示:

十字链表法
可以看到稀疏矩阵的十字链表法是通过行指针数组和列指针数组来模拟矩阵的行标和列标,通过行指针和列指针来指向矩阵中的非零元素。十字链表法会在后面的学习中详细介绍,这里我就不再继续展开,大家通过上图对该存储方法留一个印象就行。

对于稀疏矩阵的压缩存储,我们需要重点记忆它的两种存储方法——三元组存储和十字链表存储。

结语

在今天的内容中我们首先介绍了数组与矩阵的关系,当我们要将一个m×n矩阵通过二维数组存放在内存中时,我们有两种存储方式——行优先存储和列优先存储。

当我们将m×n的矩阵A通过行优先将矩阵中的各个元素放入二维数组arr[m][n]时,如果用L来表示数组中元素所占空间大小,则矩阵中的元素 a i j ( i = 1 , 2 , 3 , … … , m ; j = 1 , 2 , 3 , … … , n ) a_{ij}(i=1,2,3,……,m;j=1,2,3,……,n) aij(i=1,2,3,……,m;j=1,2,3,……,n)在内存中的地址为: L O C ( a i j ) = L O C ( a r r [ 0 ] ) + [ ( i − 1 ) × n + ( j − 1 ) ] × L LOC(a_{ij}) = LOC(arr[0]) + [(i - 1) × n + (j - 1)] × L LOC(aij)=LOC(arr[0])+[(i1)×n+(j1)]×L

当我们将m×n的矩阵A通过列优先将矩阵中的各个元素放入二维数组arr[n][m]时如果用L来表示数组中元素所占空间大小,则矩阵中的元素 a i j ( j = 1 , 2 , 3 , … … , n ; i = 1 , 2 , 3 , … … , m ) a_{ij}(j=1,2,3,……,n;i=1,2,3,……,m) aij(j=1,2,3,……,n;i=1,2,3,……,m)在内存中的地址为: L O C ( a i j ) = L O C ( a r r [ 0 ] ) + [ ( j − 1 ) × m + ( i − 1 ) ] × L LOC(a_{ij}) = LOC(arr[0]) + [(j - 1) × m + (i - 1)] × L LOC(aij)=LOC(arr[0])+[(j1)×m+(i1)]×L

之后我们介绍了什么是压缩存储——为多个值相同的元素只分配一个存储空间,对零元素不分配存储空间。其目的是为了节省空间。

并介绍了4种矩阵的压缩存储;

  1. 对称矩阵的压缩存储:
    • 通过行优先存储时矩阵元素下标与数组元素下标的关系为 k = S ( i − 1 ) ( j − 1 ) = i × ( i − 1 ) / 2 + j − 1 ( 1 < = j < = i < = n ) k=S_{(i-1)(j-1)}=i×(i-1)/2+j-1(1<=j<=i<=n) k=S(i1)(j1)=i×(i1)/2+j1(1<=j<=i<=n)
    • 通过列优先存储时矩阵元素下标与数组元素下标的关系为 k = S i j − 1 = ( 2 n − j + 2 ) ( j − 1 ) / 2 + i − j ( 1 < = j < = i < = n ) k=S_{ij}-1=(2n-j+2)(j-1)/2+i-j(1<=j<=i<=n) k=Sij1=(2nj+2)(j1)/2+ij(1<=j<=i<=n)
  2. 三角矩阵的压缩存储:
    • 通过行优先存储时矩阵元素下标与数组元素下标的关系为 k = S ( i − 1 ) ( j − 1 ) = i × ( i − 1 ) / 2 + j − 1 ( 1 < = j < = i < = n ) k=S_{(i-1)(j-1)}=i×(i-1)/2+j-1(1<=j<=i<=n) k=S(i1)(j1)=i×(i1)/2+j1(1<=j<=i<=n)
    • 通过列优先存储时矩阵元素下标与数组元素下标的关系为 k = S i j − 1 = ( 2 n − j + 2 ) ( j − 1 ) / 2 + i − j ( 1 < = j < = i < = n ) k=S_{ij}-1=(2n-j+2)(j-1)/2+i-j(1<=j<=i<=n) k=Sij1=(2nj+2)(j1)/2+ij(1<=j<=i<=n)
    • 存放常量元素的矩阵元素下标与数组元素下标的关系为 k = n ( n + 1 ) / 2 ( 1 < = i < j < = n ) k=n(n+1)/2(1<=i<j<=n) k=n(n+1)/2(1<=i<j<=n)
  3. 三对角矩阵的压缩存储:
    • 以主对角线为起点与终点的矩阵元素下标与数组元素下标之间的关系式为 k = 3 i − 3 ( 1 < = i = j < = n ) k=3i-3(1<=i=j<=n) k=3i3(1<=i=j<=n)
    • 以函数的角度计算的矩阵元素下标与数组元素下标之间的关系式为 2 i + j − 3 = k ( 1 < = i , j < = n , ∣ i − j ∣ < = 1 ) 2i+j-3=k(1<=i,j<=n,|i-j|<=1) 2i+j3=k(1<=i,j<=n,ij<=1)
  4. 稀疏矩阵的压缩存储:
    • 稀疏矩阵有两种压缩存储的方式——三元组存储和十字链表法存储。

到这里咱们今天的内容就全部结束了,希望今天的内容能够帮助大家进一步理解特殊矩阵的压缩存储的方式。如果各位朋友喜欢博主的内容,可以点赞、收藏加评论来支持一下博主,当然也可以转发给身边需要的朋友。最后感谢各位的支持,咱们下一篇再见!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/587664.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

修改Ubuntu远程登录欢迎提示信息

无论何时登录公司的某些生产系统&#xff0c;你都会看到一些登录消息、警告或关于你已登录服务器的信息&#xff0c;如下所示。 修改方式 1.打开ubuntu终端,进入到/etc/update-motd.d目录下面 可以发现目录中的文件都是shell脚本, 用户登录时服务器会自动加载这个目录中的文件…

大白话理解IoC和DI

引言 Spring是Java领域最受欢迎的开发框架之一&#xff0c;其核心功能之一就是Spring容器&#xff0c;也就是IoC容器。这篇文章&#xff0c;我们就来聊聊Spring的两大核心功能&#xff0c;控制反转&#xff08;IOC&#xff09;和依赖注入&#xff08;DI&#xff09;。 文章思…

Go 语言基础(二)【数组、切片、指针、map、struct】

1、数组 特别需要注意的是&#xff1a;在 Go 语言中&#xff0c;数组长度也是数组类型的一部分&#xff01;所以尽管元素类型相同但是长度不同的两个数组&#xff0c;它们的类型并不相同。 1.1、数组的初始化 1.1.1、通过初始化列表{}来设置值 var arr [3]int // int类型的数…

09_Scala函数和对象

文章目录 函数和对象1.函数也是对象 scala中声明了一个函数 等价于声明一个函数对象2.将函数当作对象来用&#xff0c;也就是访问函数&#xff0c;但是不执行函数结果3.对象拥有数据类型(函数类型)&#xff0c;对象可以进行赋值操作4.函数对象类型的省略写法&#xff0c;也就是…

SCI一区 | MFO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测(Matlab)

SCI一区 | MFO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测&#xff08;Matlab&#xff09; 目录 SCI一区 | MFO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测&#xff08;Matlab&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现MFO-CNN…

常见公式的几何解释

本文旨在深入探讨常见数学公式的几何意义&#xff0c;通过直观的图形和解释&#xff0c;帮助读者更好地理解并掌握这些公式的本质。文章首先概述了公式与几何图形之间的紧密联系&#xff0c;然后选取了几个典型的数学公式&#xff0c;进行详细解析。每个公式都将配以相应的几何…

vuex的学习

首先下载vuex&#xff0c;然后建立一个目录在vueX中 接着在index。js文件夹中引入 引入后导出这个文件 在main.js文件中导入&#xff0c;这样vue就有了状态管理 接着我创建了2个组件&#xff0c;在 里边规定了一个num:0 在 打印出来就可以看见 映射函数mapState&#xff0c;必…

数据结构算法——链表带环问题——数学深度解析

前言:本节内容主要是讲解链表的两个问题 &#xff1a;1、判断链表是否带环&#xff1b; 2、一个链表有环&#xff0c; 找到环的入口点。 本节内容适合正在学习链表或者链表基础薄弱的友友们哦。 我们先将问题抛出来&#xff0c;友友们可以自己去力扣或者牛客网去找相应题目&…

基于SSM的个人博客系统(四)

目录 5.3 博客类别管理模块 5.3.1 添加博客类别 5.3.2 修改博客类别 5.3.3 删除博客类别 5.3.4 显示博客类别 5.4 评论管理模块 5.4.1 审核评论 5.4.2 删除评论 前面内容请移步 基于SSM的个人博客系统&#xff08;三&#xff09; 个人博客系统的设计与实现免费源码…

头歌:Spark GraphX—寻找社交媒体中的“影响力用户”

第1关:认识Pregel API 简介 Spark GraphX中提供了方便开发者的基于谷歌Pregel API的迭代算法,因此可以用Pregel的计算框架来处理Spark上的图数据。GraphX的Pregel API提供了一个简明的函数式算法设计,用它可以在图中方便的迭代计算,如最短路径、关键路径、n度关系等,也可以…

【C++】STL学习之优先级队列

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 前言一、优先级队列的使用1.1 基本功能1.2 优先级模式切换1.3 相关题目 二、模拟实现优先级…

AI赋能不应贵气:深度解读AI助力企业渡过经济寒冬以及如何落地AI的路径

AI很棒可是给人感觉“很贵”因此我不敢用 继GPT4后Dalle3、Sora、GPT4.5、GPT5的消息以及前天突然出现的GPT 2.0&#xff08;GPT二代&#xff0c;有人说这就是OPEN AI的新产品&#xff1a;Q*&#xff09;但凡涉及到AI的一系列新闻给人予很震撼的感觉。放眼望去AI正在欣欣向荣。…

洛谷 P5854:【模板】笛卡尔树

【题目来源】https://www.luogu.com.cn/problem/P5854【题目描述】 给定一个 1∼n 的排列 p&#xff0c;构建其笛卡尔树。 即构建一棵二叉树&#xff0c;满足&#xff1a; 1.每个节点的编号满足二叉搜索树的性质。← 优先级 pri 满足二叉搜索树&#xff08;BST&#xff09;的性…

强化学习(Reinforcement learning)基本概念

概念&#xff1a; 强化学习是在与环境互动中为达到一个目标而进行的学习过程 三层结构&#xff1a; 基本元素&#xff1a;agent、environment、goal agent&#xff1a;可以理解为玩家&#xff0c;即某个游戏的参与方 environment&#xff1a;环境本身&#xff0c;可以理…

Web后端开发中对三层架构解耦之控制反转与依赖注入

内聚与耦合 内聚 比如说我们刚刚书写的员工的实现类 在这里我们仅仅书写的是和员工相关的代码 而与员工无关的代码都没有放到这里 说明内聚程度较高 耦合 以后软件开发要高内聚 低耦合 提高程序灵活性 扩拓展性 分析代码 如何解耦 创建容器 提供一个容器 存储东西 存储E…

基于FPGA的数字信号处理(5)--Signed的本质和作用

前言 Verilog中的signed是一个很多人用不好&#xff0c;或者说不太愿意用的一个语法。因为不熟悉它的机制&#xff0c;所以经常会导致运算结果莫名奇妙地出错。其实了解了signed以后&#xff0c;很多时候用起来还是挺方便的。 signed的使用方法主要有两种&#xff0c;其中一种…

Android View事件分发面试问题及回答

问题 1: 请简述Android中View的事件分发机制是如何工作的&#xff1f; 答案: 在Android中&#xff0c;事件分发机制主要涉及到三个主要方法&#xff1a;dispatchTouchEvent(), onInterceptTouchEvent(), 和 onTouchEvent(). 当一个触摸事件发生时&#xff0c;首先被Activity的…

配置 Trunk,实现相同VLAN的跨交换机通信

1.实验环境 公司的员工人数已达到 100 人&#xff0c;其网络设备如图所示。现在的网络环境导致广播较多网速慢&#xff0c;并且也不安全。公司希望按照部门划分网络&#xff0c;并且能够保证一定的网络安全性。 其网络规划如下。 PC1和 PC3为财务部&#xff0c;属于VLAN 2&…

邦注科技 温控箱对企业的重要性

注塑加工是将加热的熔融塑料注入模具中形成所需产品的工艺过程。良好的注塑加工工艺需要控制好许多参数&#xff0c;其中最重要的因素之一就是模具的温度。模具温度的不稳定会导致产品尺寸大小、表面缺陷等方面的问题&#xff0c;甚至会导致生产不良品&#xff0c;加大生产成本…

Educational Codeforces Round 165 (Rated for Div. 2 ABCDE 题)视频讲解

A. Two Friends Problem Statement Monocarp wants to throw a party. He has n n n friends, and he wants to have at least 2 2 2 of them at his party. The i i i-th friend’s best friend is p i p_i pi​. All p i p_i pi​ are distinct, and for every i ∈…