【数据结构】多维数组和广义表

在这里插入图片描述

上期回顾: 【数据结构】串
个人主页:C_GUIQU
归属专栏:数据结构

在这里插入图片描述

正文

1. 多维数组

1.1 定义与表示

1.1.1 数学定义

多维数组是线性表在维度上的扩展。从数学角度看,一个 n n n维数组可以被定义为一个由多个下标确定元素位置的元素集合。例如,二维数组 A m × n A_{m\times n} Am×n是一个包含 m × n m\times n m×n个元素的有序集合,其中每个元素 a i j a_{ij} aij 1 ≤ i ≤ m 1\leq i\leq m 1im 1 ≤ j ≤ n 1\leq j\leq n 1jn)通过两个下标 i i i j j j来唯一标识其在数组中的位置。三维数组 A l × m × n A_{l\times m\times n} Al×m×n则是一个具有 l × m × n l\times m\times n l×m×n个元素的集合,元素 a i j k a_{ijk} aijk 1 ≤ i ≤ l 1\leq i\leq l 1il 1 ≤ j ≤ m 1\leq j\leq m 1jm 1 ≤ k ≤ n 1\leq k\leq n 1kn)由三个下标确定。

1.1.2 存储结构表示

在计算机中,多维数组通常采用顺序存储方式,这意味着数组元素在内存中是连续存放的。对于二维数组,常见的存储顺序有行优先和列优先两种。

  • 行优先顺序:在行优先存储中,先存储第一行的所有元素,接着存储第二行,依此类推。例如,对于二维数组 A m × n A_{m\times n} Am×n,元素 a i j a_{ij} aij在内存中的地址计算方式为(假设每个元素占用 k k k个存储单元,且数组从地址 L O C ( a 11 ) LOC(a_{11}) LOC(a11)开始存储): L O C ( a i j ) = L O C ( a 11 ) + [ ( i − 1 ) × n + ( j − 1 ) ] × k LOC(a_{ij}) = LOC(a_{11}) + [(i - 1) \times n + (j - 1)] \times k LOC(aij)=LOC(a11)+[(i1)×n+(j1)]×k
  • 列优先顺序:列优先顺序则是先存储第一列的元素,然后是第二列,以此类推。此时,元素 a i j a_{ij} aij的地址计算公式为: L O C ( a i j ) = L O C ( a 11 ) + [ ( j − 1 ) × m + ( i − 1 ) ] × k LOC(a_{ij}) = LOC(a_{11}) + [(j - 1) \times m + (i - 1)] \times k LOC(aij)=LOC(a11)+[(j1)×m+(i1)]×k

1.2 操作与应用

1.2.1 元素访问与修改

访问和修改多维数组中的元素是常见操作。通过给定元素的下标,可以直接定位到数组中的特定元素。例如,在二维数组 A m × n A_{m\times n} Am×n中,要访问元素 a i j a_{ij} aij,在C或C++语言中可以使用 A [ i ] [ j ] A[i][j] A[i][j]的语法形式。修改元素的值也类似,只需将新值赋给指定下标的元素即可。这种操作基于数组的存储结构,通过计算元素在内存中的偏移量来实现快速访问。

1.2.2 数组遍历

遍历多维数组可以对数组中的每个元素进行处理。对于二维数组 A m × n A_{m\times n} Am×n,可以使用嵌套循环来实现遍历。以下是一个简单的遍历示例(以行优先顺序遍历并输出元素值):

for (int i = 0; i < m; i++) {
    for (int j = 0; j < n; j++) {
        std::cout << A[i][j] << " ";
    }
    std::cout << std::endl;
}

在遍历过程中,可以根据具体需求对每个元素进行各种操作,如计算元素之和、统计特定元素的个数等。

1.2.3 矩阵运算

多维数组在数学计算中常用于表示矩阵,因此矩阵运算在多维数组的应用中非常重要。常见的矩阵运算包括矩阵加法、矩阵乘法等。

  • 矩阵加法:两个具有相同行数和列数的矩阵才能进行加法运算。加法运算的结果矩阵的每个元素等于对应位置两个矩阵元素之和。例如,对于矩阵 A m × n A_{m\times n} Am×n B m × n B_{m\times n} Bm×n,其和 C m × n C_{m\times n} Cm×n的元素 c i j = a i j + b i j c_{ij}=a_{ij}+b_{ij} cij=aij+bij 1 ≤ i ≤ m 1\leq i\leq m 1im 1 ≤ j ≤ n 1\leq j\leq n 1jn)。
  • 矩阵乘法:矩阵乘法要求第一个矩阵的列数等于第二个矩阵的行数。设矩阵 A m × p A_{m\times p} Am×p B p × n B_{p\times n} Bp×n,它们的乘积 C m × n C_{m\times n} Cm×n的元素 c i j = ∑ k = 1 p a i k b k j c_{ij}=\sum_{k = 1}^{p}a_{ik}b_{kj} cij=k=1paikbkj 1 ≤ i ≤ m 1\leq i\leq m 1im 1 ≤ j ≤ n 1\leq j\leq n 1jn)。矩阵乘法在图形变换、线性代数求解方程组等领域有广泛应用。

1.3 多维数组的特点

1.3.1 数据存储的连续性

多维数组在内存中按顺序存储元素,这种连续性有利于快速访问元素。通过计算元素的下标偏移量,可以直接定位到内存中的元素位置,从而实现高效的数据读取和写入。然而,这种存储方式也使得在数组中间插入或删除元素时比较困难,因为需要移动大量后续元素来保持存储的连续性。

1.3.2 固定的维度和大小

在定义多维数组时,通常需要指定数组的维度和每个维度的大小。一旦定义,数组的维度和大小就固定了,不能动态改变。这在某些情况下可能会限制数组的灵活性,但也使得数组在存储和访问元素时具有较高的效率,因为编译器可以预先计算出元素的地址偏移量等信息。

1.3.3 适合表示多维数据关系

多维数组能够自然地表示具有多维关系的数据。例如,在图像处理中,图像的像素数据可以用二维数组表示,其中行和列分别对应图像的坐标位置;在三维空间数据处理中,如物体的坐标、温度场等数据,可以用三维数组来表示,每个维度对应空间的一个方向。这种表示方式使得数据的组织和处理更加直观和方便,便于进行各种基于多维关系的计算和操作。

2. 广义表

2.1 定义与概念

2.1.1 广义表的定义

广义表是一种非线性的数据结构,它是线性表的推广。广义表中的元素既可以是单个元素(称为原子),也可以是另一个广义表。广义表通常记作 L S = ( a 1 , a 2 , ⋯   , a n ) LS=(a_1,a_2,\cdots,a_n) LS=(a1,a2,,an),其中 L S LS LS是广义表的名称, a i a_i ai 1 ≤ i ≤ n 1\leq i\leq n 1in)可以是原子或子广义表。例如, L = ( a , ( b , c , d ) , e ) L=(a,(b,c,d),e) L=(a,(b,c,d),e)就是一个广义表,其中 a a a e e e是原子, ( b , c , d ) (b,c,d) (b,c,d)是一个子广义表。

2.1.2 表头与表尾

广义表有表头和表尾的概念。表头是广义表的第一个元素,而表尾是广义表中除表头之外的其余元素组成的广义表。对于广义表 L = ( a , ( b , c , d ) , e ) L=(a,(b,c,d),e) L=(a,(b,c,d),e),其表头为 a a a,表尾为 ( ( ( b , c , d ) , e ) ) (((b,c,d),e)) (((b,c,d),e))。需要注意的是,表尾一定是一个广义表,即使表尾只包含一个元素,也需要用括号括起来表示。

2.1.3 原子与子表

  • 原子:广义表中不可再分割的元素称为原子。在上述广义表 L = ( a , ( b , c , d ) , e ) L=(a,(b,c,d),e) L=(a,(b,c,d),e)中, a a a b b b c c c d d d e e e都是原子。
  • 子表:广义表中的子广义表称为子表。如 ( b , c , d ) (b,c,d) (b,c,d) L = ( a , ( b , c , d ) , e ) L=(a,(b,c,d),e) L=(a,(b,c,d),e)的子表。

2.2 存储结构

2.2.1 链式存储结构

广义表常用的存储结构是链式存储。在链式存储结构中,每个节点包含两个域:标志域和值域。

  • 标志域:用于区分节点是原子节点还是子表节点。若标志域的值为 0 0 0,表示该节点是原子节点,值域中存储原子的值;若标志域的值为 1 1 1,表示该节点是子表节点,值域中存储指向子表的指针。
  • 值域:根据标志域的不同,值域的含义也不同。对于原子节点,值域存储原子的值;对于子表节点,值域存储指向子表的指针,该指针指向子表的第一个节点。

2.2.2 广义表的深度

广义表的深度是指广义表中括号的最大嵌套层数。计算广义表深度可以采用递归的方法。空广义表的深度为 1 1 1;只包含原子的广义表深度也为 1 1 1;对于包含子表的广义表,其深度等于子表中最大深度加 1 1 1。例如,广义表 L = ( a , ( b , c , d ) , e ) L=(a,(b,c,d),e) L=(a,(b,c,d),e)的深度为 3 3 3,因为子表 ( b , c , d ) (b,c,d) (b,c,d)的深度为 2 2 2,加上外层括号,总深度为 3 3 3

2.3 基本操作

2.3.1 取表头和表尾操作

取表头操作返回广义表的第一个元素,取表尾操作返回除表头之外的其余元素组成的广义表。在链式存储结构下,实现这些操作需要根据节点的标志域和指针来正确获取相应的元素或子表。例如,对于广义表 L = ( a , ( b , c , d ) , e ) L=(a,(b,c,d),e) L=(a,(b,c,d),e),取表头操作返回原子 a a a(如果是原子节点)或指向子表 ( b , c , d ) (b,c,d) (b,c,d)的指针(如果是子表节点);取表尾操作则需要构建一个新的广义表,包含原广义表中除第一个节点之外的所有节点。

2.3.2 广义表的遍历

广义表的遍历可以通过递归方式实现。如果当前节点是原子节点,则直接处理该原子(如输出其值);如果当前节点是子表节点,则递归地遍历该子表。例如,对于广义表 L = ( a , ( b , c , d ) , e ) L=(a,(b,c,d),e) L=(a,(b,c,d),e),遍历过程如下:首先访问原子 a a a,然后递归遍历子表 ( b , c , d ) (b,c,d) (b,c,d)(即访问 b b b c c c d d d),最后访问原子 e e e

2.3.3 广义表的复制

广义表的复制操作需要复制广义表中的每个元素,包括原子和子表。对于原子,直接复制其值;对于子表,需要递归地复制子表的结构和元素。在链式存储结构下,复制广义表可以通过创建新的节点,并按照原广义表的结构和元素值进行赋值来实现。例如,要复制广义表 L = ( a , ( b , c , d ) , e ) L=(a,(b,c,d),e) L=(a,(b,c,d),e),首先创建一个新的节点用于存储原子 a a a,然后递归地复制子表 ( b , c , d ) (b,c,d) (b,c,d),最后创建节点存储原子 e e e,并正确构建节点之间的链接关系,从而得到一个与原广义表结构和元素完全相同的新广义表。

2.4 广义表的应用

2.4.1 在符号运算中的应用

在符号运算中,广义表可用于表示代数表达式。例如,表达式 x + y ∗ ( z − w ) x + y * (z - w) x+y(zw)可以用广义表表示为 ( ( + , x , ( ∗ , y , ( − , z , w ) ) ) ) ((+,x,(*,y,(-,z,w)))) ((+,x,(,y,(,z,w))))。其中,运算符作为广义表的元素,操作数可以是原子(如变量 x x x y y y z z z w w w)或子广义表(如 ( − , z , w ) (-,z,w) (,z,w) ( ∗ , y , ( − , z , w ) ) (*,y,(-,z,w)) (,y,(,z,w)))。通过广义表的操作,可以方便地对代数表达式进行求值、化简、求导等运算。例如,求导运算可以根据求导规则递归地对广义表中的每个元素进行处理,根据元素是原子还是子表应用相应的求导公式,最终得到求导后的广义表表示的表达式。

2.4.2 在人工智能领域的应用

在人工智能领域,广义表常用于知识表示。例如,在专家系统中,可以用广义表来表示规则和事实。一条规则如“如果动物有羽毛且会下蛋,那么它是鸟类”可以表示为广义表 ( ( r u l e , ( h a s − f e a t h e r , a n i m a l ) , ( l a y s − e g g s , a n i m a l ) , ( i s − b i r d , a n i m a l ) ) ) ((rule,(has-feather,animal),(lays-eggs,animal),(is-bird,animal))) ((rule,(hasfeather,animal),(layseggs,animal),(isbird,animal))),其中 r u l e rule rule表示规则,后面的子表分别表示规则的条件和结论。通过对广义表形式的知识进行推理和匹配,可以实现专家系统的决策和判断功能,帮助解决各种复杂的问题,如医疗诊断、故障诊断等。广义表的层次结构和灵活性使其能够有效地组织和处理复杂的知识体系,为人工智能系统提供了一种强大的知识表示手段。

2.4.3 在数据结构转换中的应用

广义表可以作为一种中间数据结构,用于在不同数据结构之间进行转换。例如,将树结构转换为广义表形式,可以方便地对树进行存储、传输和处理。在某些情况下,将一种复杂的数据结构转换为广义表后,可以利用广义表的操作来实现对原数据结构的一些操作,然后再将广义表转换回原数据结构或其他目标数据结构。这种转换方式在数据处理和算法设计中具有一定的灵活性和实用性。

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Guiat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值