数据结构

CH5 数组和广义表

滨州学院 庄波

目标要求

  • 掌握数组的存储结构及特殊矩阵的压缩存储;
  • 掌握广义表的概念;
  • 了解广义表的基本运算及存储结构。

5.1 数组的定义

数组 ADT

  • n 维数组
    • 类型相同的数据元素
    • \(n\) 维下标
      • 创建之后维数不再改变
      • \(i\) 维的长度 \(b_i\)
      • 元素个数 \(b_1 \times b_2 \times \cdots \times b_n\)
    • \(n\) 个线性关系
  • 基本操作
    • 初始化
    • 销毁
    • 存取元素
    • 修改元素值

5.2 数组的顺序表示和实现

数组的顺序存储

  • 多维数组映射到一维存储空间
  • 次序约定
    • 低下标优先
    • 高下标优先
  • 低下标优先
    • 优先按低维下标的大小安排存储空间
    • 二维数组:
      • 以行序为主序
      • 如 C/C++ 等大多数语言
  • 高下标优先
    • 优先按高维下标的大小安排存储空间
    • 二维数组:
      • 以列序为主序
      • 如 Fortran
  • 数组的顺序存储的特点:
    • 随机存取
    • 存取数组中任一元素时间相等

5.3 矩阵的压缩存储

特殊矩阵

  • 对称矩阵\(a_{ij} = a_{ji}, \quad 1 \le i,j \le n\)
    • 下三角部分 a[i,j] 映射到 sa[k]
    • \(k \ge 0\)\(1 \le i,j \le n\) 满足以下关系:

\[k = \begin{cases} \dfrac{ i (i-1) }{ 2 } + j-1, & i \ge j, \\ \dfrac{ j (j-1) }{ 2 } + i-1, & i<j. \end{cases}\]

  • 三角矩阵
    • 与对称矩阵类似
  • 下三角矩阵 a[i,j] 映射到 sa[k],满足

\[k = \begin{cases} \dfrac{ i (i-1) }{ 2 } + j-1, & i \ge j, \\ 0, & i<j. \end{cases}\]

  • 上三角矩阵 a[i,j] 映射到 sa[k],满足

\[k = \begin{cases} 0, & i < j, \\ \dfrac{ j (j-1) }{ 2 } + i-1, & i\le j. \end{cases}\]

  • 对角矩阵
    • 非零元集中在主对角线为中心的带状区域
    • 也可以压缩存储到一维数组上

稀疏矩阵

稀疏因子与稀疏矩阵
\(m \times n\)的矩阵中有 \(t\) 个元素不为零,定义稀疏因子 \(\delta\)
\[\delta = \frac{t}{m \times n}\]
通常认为 \(\delta \le 0.05\) 时称为稀疏矩阵
  • 稀疏矩阵的压缩存储:只存储非零元
    • (1)三元组顺序表
    • (2)行逻辑链接的顺序表
    • (3)十字链表

三元组顺序表

三元组
矩阵中一个元素的位置和值 (i,j,e)
三元组顺序表
以行序为主序
先按 i 排序,再按 j 排序
  • 三元组顺序表的类型定义
/// 三元组
struct Triple {
  int i,j;  // 非零元的位置
  E e;      // 元素的值
};

/// 三元组顺序表
struct TSMatrix {
  Triple data[MAXSIZE];
  int mu, nu, tu;  // 矩阵行数、列数和非零元数
}
  • 算法:矩阵转置

  • 思路:
    • 先算出每一列中非零元的个数
    • 再计算每一列中第一个非零元的位置
    • 逐个元素转置
  • 时间复杂度:\(O(nu+tu)\)

行逻辑链接的顺序表

  • 行逻辑链接的顺序表
    • 存储各行第一个非零元的位置
    • 算法:矩阵乘法

十字链表

  • 结点结构:( i, j, e, right, down )
  • 非零元素组成行表和列表
  • 算法:创建十字链表

5.4 广义表的定义

广义表的定义

  • 广义表\(L=(\alpha_1,\alpha_2,\ldots, \alpha_n)\),线性表的推广
  • ADT 定义
    • 数据对象:数据元素可以是原子广义表
    • 数据关系:线性关系
    • 基本操作
长度
表中元素个数
空表 ( )
长度为零的表
与 (( )) 不同
表头 Head(L)
表中的第一个元素
可能是原子,也可能是列表
表尾 Tail(L)
除第一个之外的其余元素组成的表
一定是列表
特点
列表的元素可以是子表
列表可以为其他列表共享
列表可是是一个递归表

5.5 广义表的存储结构

广义表的存储结构

  • 通常采用链式存储结构
  • 结点结构:
    • 表结点(tag=1, hp, tp)
    • 原子结点(tag=0, atom)
/// 广义表结点
struct GLNode {
  int tag;  // ATOM=0 原子, LIST=1 子表
  union {
    E atom;  // 原子结点的值
    struct {
      GLNode *hp, *tp; // 表头指针和表尾指针
    } ptr;   // 表结点的指针
  };
};

/// 广义表
typedef GLNode *GList;

5.7 广义表的递归算法

广义表的递归算法

  • 广义表具有递归结构
  • 便于采用递归算法
    • 空表和原子通常作为递归停止条件
    • 对子表递归
  • 算法:求广义表的深度(括号嵌套的最大重数)
  • 算法:复制广义表
  • 算法:建立广义表

习题

  • 5.1 二维数组 A[10..20, 5..10] 采用以行为主的方法存储,每个元素占 4 个存储单元,并且 A[10, 5] 的存储地址是 1000,则 A[18, 9]的地址为____
  • 5.2 n 阶对称矩阵中的元素 a[i,j] 压缩存储到一维数组 sa[k],以行序为主序存储其下三角中的元,写出 k 与 i 和 j 的对应关系。
  • 5.3 广义表 L = ((a,b),(c,d)),则 GetHead(L)=____,GetTail(L)=____,GetTail(GetHead(L))=____,GetTail(GetHead(GetTail(L)))=____
  • 5.4 广义表 L = ((()),a,((b,c),(),d),(((e)))) 的深度为 ____