1.数组
-
数组是定长线性表在维度上的扩展,即线性表中的元素又是一个线性表。N维数组是一种"同构"的数据结构,其每个数据元素类型相同、结构一致。
-
其可以表示为行向量形式或者列向量形式线性表,单个关系最多只有一个前驱和一个后继,本质还是线性的。
-
数组结构的特点:
数据元素数目固定;数据元素类型相同;数据元素的下标关系具有上下界的约束且下标有序 -
数组数据元素固定,一般不做插入和删除运算,适合于采用顺序结构
数组存储地址的计算,特别是二维数组,要注意理解,假设每个数组元素占用存储长度为len,起始地址为a,存储地址计算如下(默认从0开始编号)
1. 一维数组
| 项目 | 说明 | 公式 |
|---|---|---|
| 定义 | a[n],包含 n 个元素 |
|
| 元素 | a[0], a[1], ..., a[n-1] |
|
| 首地址 | a(假设为基地址) |
|
| 元素大小 | len(每个元素占用的字节数) |
|
| 地址计算 | 元素 a[i] 的地址 |
a + i × len |
2. 二维数组
基本定义
- 数组定义:
a[m][n](m行,n列) - 元素总数:
m × n个 - 索引范围:
a[0][0]到a[m-1][n-1]
地址计算表格
| 存储方式 | 元素地址公式 | 说明 |
|---|---|---|
| 按行存储 (C/C++/Java/Python等) | a + (i × n + j) × len |
先行后列,一行接一行 |
| 按列存储 (Fortran/Matlab等) | a + (j × m + i) × len |
先列后行,一列接一列 |
参数说明:
a: 数组首地址(a[0][0]的地址)i: 行索引(0 ≤ i < m)j: 列索引(0 ≤ j < n)len: 每个元素占用的字节数
3. 示例对比
假设:a[3][4](3行4列),len = 4字节,a = 1000
| 元素 | 按行存储地址计算 | 按列存储地址计算 |
|---|---|---|
a[1][2] |
1000 + (1×4 + 2)×4 = 1000 + 6×4 = 1024 | 1000 + (2×3 + 1)×4 = 1000 + 7×4 = 1028 |
a[2][1] |
1000 + (2×4 + 1)×4 = 1000 + 9×4 = 1036 | 1000 + (1×3 + 2)×4 = 1000 + 5×4 = 1020 |
4. 存储示意图
按行存储(行主序)
内存布局:a[0][0], a[0][1], a[0][2], a[0][3],
a[1][0], a[1][1], a[1][2], a[1][3],
a[2][0], a[2][1], a[2][2], a[2][3]
按列存储(列主序)
内存布局:a[0][0], a[1][0], a[2][0],
a[0][1], a[1][1], a[2][1],
a[0][2], a[1][2], a[2][2],
a[0][3], a[1][3], a[2][3]
5. 重要注意事项
- 下标从0开始:以上公式假设数组下标从0开始
- 边界检查 :实际使用中需要确保
0 ≤ i < m,0 ≤ j < n - 语言差异 :
- 行主序语言:C, C++, Java, Python (numpy), JavaScript
- 列主序语言:Fortran, MATLAB, R, Julia
- 高维数组:三维及以上的数组可以类似推导
三维数组示例 a[x][y][z]:
- 按行存储:
a + (i×y×z + j×z + k) × len - 按列存储:
a + (k×x×y + j×x + i) × len
2.矩阵
-
特殊矩阵:矩阵中的元素(或非0元素)的分布有一定的规律。常见的特殊矩阵有对称矩阵、三角矩阵和对角矩阵。
-
稀疏矩阵:在一个矩阵中,若非零元素的个数远远少于零元素个数,且非零元素的分布没有规律。
-
存储方式为三元组结构,即存储每个非零元素的(行,列,值)。
对于考试真题,将矩阵求按行(或按列)存储在一维数组,求矩阵i,j和一维数组下标k关系,可以代入特定值,比如i=0,j=0,k=x等,到题目给出关系式,验证是否正确。
3.广义表
您的描述非常准确和全面!您已经清晰地定义了广义表的核心概念和特性。让我在您的基础上做一些补充和归纳:
核心要点总结
1. 广义表 vs 线性表
- 线性表:元素都是原子(不可再分)
- 广义表:元素可以是原子或子表(可再分)
- 线性表是广义表的特例(所有元素都是原子时)
2. 基本概念
- 长度:最外层元素的个数
- 深度:括号嵌套的最大层数
- 空表 :长度为0的表,记为
() - 原子:不可再分的基本数据元素
3. 重要特性
递归性:
- 广义表本身可以是另一个广义表的子表
- 广义表可以被其自身的子表共享
- 广义表可以是一个递归的表
存储结构 :
通常采用链式存储,每个结点有两个域:
- tag域:标识是原子(0)还是子表(1)
- union域:存储原子值或指向子表的指针
- next域:指向下一个元素
4. head() 和 tail() 操作详解
表头 head(LS):
- 非空广义表的第一个元素
- 可以是原子或子表
- 示例:
head((a,b,c)) = a
表尾 tail(LS):
- 除第一个元素外,剩余元素构成的表
- 总是一个表(即使只有一个元素)
- 示例:
tail((a,b,c)) = (b,c)
5. 重要示例
text
A = () # 空表,长度=0,深度=1
B = (e) # 长度=1,深度=1
C = (a, (b, c, d)) # 长度=2,深度=2
D = (A, B, C) # 长度=3,深度=3
E = (a, E) # 递归表,长度=2,深度=∞
6. 特殊广义表
- 纯表:所有元素都是原子(等价于线性表)
- 再入表:允许元素被共享
- 递归表:允许直接或间接递归引用
7. 应用领域
- LISP/Scheme等函数式语言:程序和数据都用广义表表示
- 符号计算:处理数学表达式
- 人工智能:知识表示、模式匹配
- 数据库:嵌套关系的表示
深度 vs 长度
长度 (Length)
- 定义:最外层元素的个数
- 计算方法:数最外层逗号分隔的元素个数 + 1
- 示例 :
(a, b, c)→ 长度 = 3((a, b), c, (d, e))→ 长度 = 3(虽然有三个元素,第一个和第三个是子表)()→ 长度 = 0
深度 (Depth)
- 定义:括号嵌套的最大层数(原子的深度为0)
- 计算方法:从最外层到最内层原子的括号层数
- 示例 :
(a, b, c)→ 深度 = 1((a, b), c)→ 深度 = 2(((a)))→ 深度 = 3
对比示例
| 广义表 | 长度 | 深度 | 说明 |
|---|---|---|---|
() |
0 | 1 | 空表特殊:深度为1 |
(a) |
1 | 1 | 只有原子 |
(a, b, c) |
3 | 1 | 多个原子 |
((a, b)) |
1 | 2 | 一个子表,包含两个原子 |
(a, (b, c)) |
2 | 2 | 第一个是原子,第二个是子表 |
((a, (b)), c) |
2 | 3 | 嵌套更多层 |
((((a)))) |
1 | 4 | 多层嵌套 |
重要区别
- 长度关注"广度":同一层有多少元素
- 深度关注"深度":最多嵌套了多少层
特殊情况
空表 ():
- 长度 = 0(没有元素)
- 深度 = 1(这是定义规定的)
递归表:
text
L = (a, L) # 长度=2,深度=∞(无限)
计算技巧
计算深度:
深度 = 1 + max(各元素的深度)
空表深度 = 1
原子深度 = 0
计算长度:
长度 = 最外层逗号数 + 1
(不包括子表内部的逗号)
总结
- 长度 = 水平方向有多少个元素
- 深度 = 垂直方向最多有多少层嵌套
它们分别描述了广义表在水平和垂直两个维度上的大小,是完全正交的两个概念。