0、思维导图

数据结构第4章 数组和广义表-LMLPHP

1、数组与特殊矩阵

1)基础知识

在数据结构中,数组特殊矩阵是基础且重要的概念,它们在存储和处理数据方面扮演着核心角色。以下是这两个概念的基础知识:

1️⃣数组(Array)

数组是一种线性数据结构,用于存储具有相同数据类型的元素的集合。数组中的每个元素都可以通过索引(或位置)直接访问。在内存中,数组的元素是的。

  • 访问元素:可以通过索引直接访问数组中的元素。如果数组的首索引为0,则第 n n n 个元素的索引为 n − 1 n-1 n1。访问时间复杂度为 O ( 1 ) O(1) O(1)

  • 插入和删除:在数组中插入或删除元素通常需要移动元素,以保持元素的连续性。因此,这些操作的时间复杂度为 O ( n ) O(n) O(n),其中 n n n 是数组中的元素数量。

  • 优点:直接索引访问;内存利用率高。

  • 缺点:大小固定;插入和删除操作效率低。

2️⃣特殊矩阵

特殊矩阵是指具有特定规律或属性的,这使得它们可以用更优化的方式存储和处理。例如:

  1. 对角矩阵:除了主对角线外,其他元素都是0。可以用一个一维数组存储非零元素。

  2. 三角矩阵

    • 上三角矩阵:所有在主对角线以下的元素都是0。

    • 下三角矩阵:所有在主对角线以上的元素都是0。

    • 上三角和下三角矩阵可以通过压缩存储来优化空间,只存储非零元素。

  3. 稀疏矩阵:大部分元素为0的矩阵。可以用多种方式(如三元组表或链表)高效存储非零元素和它们的位置信息,从而节省空间。

  4. 对称矩阵 a [ i ] [ j ] = a [ j ] [ i ] a[i][j] = a[j][i] a[i][j]=a[j][i] 对所有 i i i j j j。只需存储上三角或下三角的元素。

3️⃣存储特殊矩阵

对于特殊矩阵,可以采取优化的存储方法,以减少存储空间和提高处理效率。例如,稀疏矩阵的压缩存储可以通过记录非零元素的值及其索引来实现。这种方法在处理大规模稀疏矩阵时特别有效。

了解好基础知识后,来看一下其存储方式:

2)顺序存储

1️⃣行优先存储

数组每一行元素在内存中是连续的,即先存储第一行,再存储第二行,依次类推。

数据结构第4章 数组和广义表-LMLPHP

计算公式:LOC(i,j) = LOC(0,0) + [i * n + j] *L;

2️⃣列优先存储

数组的每一列元素在内存中是连续的,即先存储第一列,再存储第二列,依次类推。

数据结构第4章 数组和广义表-LMLPHP

计算公式LOC(i,j) = LOC(0,0) + [j * m + i] *L;

3️⃣公式说明

  • 设数组为A[m][n],求A[i][j]元素的存储地址

  • 存储单元大小L

  • 数组下标从0开始,如若从1开始,要注意区分,可能需要-1

4️⃣为什么有行列优先之分?

主要取决于程序对数组的访问模式和计算需求。

  • 如果程序主要是按照行进行遍历或操作的话,那么使用行优先存储会更加高效,因为这样可以减少内存地址的跳转次数,提高缓存命中率。
  • 如果程序主要是按照列进行遍历或操作的话,那么使用列优先存储会更加高效,原理同上。

另外,不同的数组存储方式也会影响到矩阵运算的结果,因为矩阵乘法涉及到行和列的交换,所以需要注意矩阵的转置和顺序问题。

3)特殊矩阵的压缩存储

1️⃣对称矩阵

  • 元素关于主对角线对称位置的元素值相等

数据结构第4章 数组和广义表-LMLPHP

  • 例如: ( 1 3 3 2 ) , ( 2 5 6 5 0 7 6 7 3 ) \begin{pmatrix} 1 & 3 \\ 3 & 2 \\ \end{pmatrix} , \quad \begin{pmatrix} 2 & 5 & 6 \\ 5 & 0 & 7 \\ 6 & 7 &3 \end{pmatrix} (1332), 256507673

2️⃣三角矩阵

  • 以上或以下元素全为0的矩阵

    • 以上全为0称为:下三角矩阵

    • 以下全为0称为:上三角矩阵

数据结构第4章 数组和广义表-LMLPHP

  • 例如: ( 1 0 4 2 ) , ( 2 0 0 0 0 0 4 6 3 ) \begin{pmatrix} 1 & 0 \\ 4 & 2 \\ \end{pmatrix} , \quad \begin{pmatrix} 2 & 0 & 0 \\ 0 & 0 & 0 \\ 4 & 6 &3 \end{pmatrix} (1402), 204006003

3️⃣三对角矩阵

  • 当|i-j|>1(行列下标求差,结果大于1)时, a i j a_{ij} aij=0的矩阵

  • 只有主对角线及其两侧的元素不为零

  • 数据结构第4章 数组和广义表-LMLPHP

  • 例如: ( a 11 a 12 a 21 a 22 a 23 a 32 a 33 a 34 a 43 a 44 ) \begin{pmatrix} a_{11} & a_{12} & & \\ a_{21} & a_{22} & a_{23} & \\ & a_{32} & a_{33} & a_{34} \\ & & a_{43} & a_{44} \end{pmatrix} a11a21a12a22a32a23a33a43a34a44

4)稀疏矩阵的压缩存储及运算

1️⃣稀疏矩阵

非零元素的个数 <<(远小于) 零元素的个数,并且非零元素分布无规律的矩阵。

2️⃣压缩存储

为了节约空间和提高效率,稀疏矩阵通常采用的方式,即只存储非零元素及其位置信息。

a.三元组顺序表

数据结构第4章 数组和广义表-LMLPHP

b.顺序表

3️⃣基本运算

转置、加法、乘法等基本运算。

2、广义表

1)基本概念

1️⃣广义表

一种线性存储结构,它可以存储不可再分的原子或者其他广义表,形式上类似于一个有限序列。例如: L S = ( a 1 , a 2 , . . . , a n ) L_S = (a_1,a_2,...,a_n) LS=(a1,a2,...,an)

2️⃣广义表深度

括号层数

3️⃣广义表长度

它所包含的元素个数。

2)存储结构

广义表是一种特殊的线性表,它允许元素既可以是单个元素,也可以是另一个广义表。因此,广义表的存储结构需要能够表示这种嵌套关系。

广义表的存储结构主要有两种:

头尾链表和扩展线性链表存储结构。

1️⃣头尾链表存储

头尾链表存储结构使用两个指针来表示一个广义表:

  • 表头指针:指向广义表第一个元素的指针
  • 表尾指针:指向广义表最后一个元素的指针

广义表中的每个元素都用一个结点来表示,结点包含以下两个域:

  • 数据域:存储元素的值
  • 下一结点指针:指向下一个元素的指针

对于原子元素,其下一结点指针为NULL。对于子表元素,其下一结点指针指向子表的表头结点。

a.优点:

  • 存储结构简单,易于理解和实现
  • 插入和删除操作比较方便

b.缺点:

  • 查找操作比较困难,需要遍历整个广义表
  • 空间利用率不高,因为每个结点都需要存储一个下一结点指针

2️⃣扩展线性链表存储

扩展线性链表存储结构使用一个数组来表示一个广义表。数组中的每个元素可以是原子元素,也可以是子表的地址。

对于原子元素,其地址直接存储在数组中。对于子表元素,其地址指向子表在数组中的起始位置。

a.优点:

  • 查找操作比较方便,可以直接通过地址访问数组中的元素
  • 空间利用率比较高

b.缺点:

  • 插入和删除操作比较困难,需要移动数组中的元素

两种存储结构的比较如下表:

选择哪种存储结构取决于具体的应用需求。

  • 如果需要频繁地进行查找操作,则可以使用扩展线性链表存储结构;
  • 如果需要频繁地进行插入和删除操作,则可以使用头尾链表存储结构。

3)基本运算

广义表(Generalized List)是线性表的一种推广。在广义表中,元素既可以是单个元素,也可以是另一个广义表。广义表的两个基本运算是 GetHead 和 GetTail:

  • GetHead:获取广义表的第一个元素。如果这个元素是另一个广义表,那么返回的就是这个子表。
  • GetTail:获取除了第一个元素外,广义表中剩余的所有元素构成的广义表。

总结来看如下:

1️⃣求表头GetHead(L)

  • 非空广义表的第一个元素

  • 可以是一个

2️⃣求表尾GetTail(L)

  • 非空广义表除表头元素以外其它元素构成的表

  • 一定是一个

3️⃣举例

假设我们有以下广义表 L L L

L = ( a , ( b , c ) , d ) L = (a, (b, c), d) L=(a,(b,c),d)

在这个例子中, L L L 包含三个元素: a a a,子表 ( b , c ) (b, c) (b,c),和 d d d

a.GetHead 运算

对广义表 L L L 执行 GetHead 运算将返回广义表的第一个元素:

GetHead ( L ) = a \text{GetHead}(L) = a GetHead(L)=a

b.GetTail 运算

对广义表 L L L 执行 GetTail 运算将返回除了第一个元素之外的所有元素构成的广义表:

GetTail ( L ) = ( ( b , c ) , d ) \text{GetTail}(L) = ((b, c), d) GetTail(L)=((b,c),d)

在执行 GetTail 运算后,我们得到一个新的广义表,它包含了原广义表的第二个和第三个元素。这里需要注意,GetTail 运算的结果是一个广义表,而非元素,即使只有一个元素,其仍为表。

4)广义表与线性表的联系

  • 广义表 <-------> 线性表的推广

  • 线性表 <-------> 广义表的特例

02-20 12:56