数据结构基础与算法图解(6)—— 数组和广义表

6.数组和广义表

6.1 数组

介绍数组的抽象数据类型

ADT Array{
    数据对象:
        D={aj1,j2...ji|ji=0,....bi-1,i=1,2,....n,n(>0)是数组的维数,bi是数组第i维的长度}
    数据关系:
        R={R1,R2,....,RN}
    	RI={<aj1,j2,...,aji,j+1,...jn|0<=jk<=bk-1,1<=k<=n且k≠i,0<=j1<=bi-2,i=1,2,...,n}
    基本操作:
        InitArray()
        若维数n和各维长度合法,则构造相应的数组A,并返回OK。
        DestroyArray(&A)
        销毁数组A
        Value(A,&e,index1,...,indexn)
        初始条件:A是n维数组,e为元素变量,随后是n个下标值
        操作结果:若各下标不超界,则e赋值为所指定的A的元素值,并返回OK
        Assign(&A,e,index1,...,indexn)
        初始条件:A是n维数组,e为元素变量,随后是n个下标值。
        操作结果:若下标不超界,则将e的值赋给所指定的A的元素,并返回OK
}ADT Array

6.1.1数组的顺序实现

两种顺序映像方式:

  1. 以行序为主序(低下标优先)

    二维数组A中任一元素ai,j 的存储位置
    LOC(i,j) = LOC(0,0) + (b2×i+j)×L

  2. 以列序为主序(高下标优先)

推广到一般情况,可得到 n 维数组数据元素存储位置的映象关系。

LOC(j1, j2, …, jn ) = LOC(0,0,…,0) + ∑ ci ji

其中 cn = L,ci-1 = bi ×ci , 1 < i <= n

称为 n 维数组的映象函数, 数组元素的存储位置是其下标的线性函数。

6.1.1.1 例题
  1. 设有数组A[i,j],数组的每个元素长度为3字节,i的值为1到8,j的值为1到10,数组从内存首地址BA开始顺序存放,当用以列为主存放时,元素A[5,8]的存储首地址为( B )。
    A.BA+141 B.BA+180
    C.BA+222 D.BA+225
  2. 设有二维数组A[0…9,0…19],其每个元素占两个字节,第一个元素的存储地址为100,若按列优先顺序存储,则元素A[6,6]存储地址为(232)。
  3. 假设以行序为主序存储二维数组A=array[1…100,1…100],设每个数据元素占2个存储单元,基地址为10,则LOC[5,5]=( B )。
    A. 808 B. 818 C. 1010 D. 1020

6.2 特殊矩阵

6.2.1 稀疏矩阵

假设 m 行 n 列的矩阵含 t 个非零元素,则称
σ=t/(m*n)

为稀疏因子,通常认为 σ<= 0.05 的矩阵为稀疏矩阵。

以常规方法,即以二维数组表示高阶的稀疏矩阵时产生的问题:

  1. 零值元素占了很大空间;
  2. 计算中进行了很多和零值的运算,遇除法还需判别除数是否为零。
6.2.1.1 特殊矩阵

三角矩阵
对称矩阵
对角矩阵

6.2.1.2 随机稀疏矩阵

非零元在矩阵中随机出现。

6.2.2 稀疏矩阵的转置

6.2.2.1 常规算法
//用常规的二维数组表示时的算法
   for (col=1; col<=nu; ++col)
        for (row=1; row<=mu; ++row)
          T[col][row] = M[row][col];
//其时间复杂度为: O(mu×nu)
6.2.2.2 三元组顺序表

在这里插入图片描述

#define  MAXSIZE  12500
 typedef struct {
     int  i, j;      //该非零元的行下标和列下标
     ElemType  e;    // 该非零元的值
 } Triple;  // 三元组类型
typedef union {
     Triple  data[MAXSIZE + 1]; 
      int     mu, nu, tu; 
} TSMatrix;  // 稀疏矩阵类型


//快速转置算法
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T){
  T.mu = M.nu;  T.nu = M.mu;  T.tu = M.tu;
  if (T.tu) {
    for (col=1; col<=M.nu; ++col)  num[col] = 0;
    for (t=1; t<=M.tu; ++t)  ++num[M.data[t].j];
    cpot[1] = 1;
    for (col=2; col<=M.nu; ++col)
       cpot[col] = cpot[col-1] + num[col-1];
    for (p=1; p<=M.tu; ++p) {转置矩阵元素}
  } // if
  return OK;
} // FastTransposeSMatrix


// O(M.nu+M.tu)

6.3 广义表

广义表是递归定义的线性结构,
LS = ( a1, a2,…, an )
其中:i 或为原子 或为广义表。
例如: A = ( )
F = (d, (e))
D = ((a,(b,c)), F)
C = (A, D, F)
B = (a, B) = (a, (a, (a, … , ) ) )

//广义表的抽象数据类型

ADT Glist {
  数据对象:D={ei | i=1,2,..,n;  n≥0; 
                    ei∈AtomSet 或 ei∈GList,
                    AtomSet为某个数据对象  }
  数据关系:
            LR={<ei-1, ei >| ei-1 ,ei∈D, 2≤i≤n}
  基本操作:
   //结构的创建和销毁
   InitGList(&L);      DestroyGList(&L);
   CreateGList(&L, S);   CopyGList(&T, L);
   //状态函数
   GListLength(L);   GListDepth(L);
   GListEmpty(L);   GetHead(L);    GetTail(L);
   //插入和删除操作
   InsertFirst_GL(&L, e);
   DeleteFirst_GL(&L, &e);
   //遍历
   Traverse_GL(L, Visit());
} ADT Glist

6.3.1 例题

  1. 广义表((a,b,c,d))的表头是( C ),表尾是( B )。
    A. a B.() C.(a,b,c,d) D.(b,c,d)
  2. 下面说法不正确的是( A )。
    A. 广义表的表头总是一个广义表 B. 广义表的表尾总是一个广义表
    C. 广义表难以用顺序存储结构 D. 广义表可以是一个多层次的结构
  3. 当广义表中的每个元素都是原子时,广义表便成了_______。线性表
  4. 广义表的表尾是指除第一个元素之外,_______。其余元素组成的表
  5. 设广义表L=((a,b,c)),则L的长度和深度分别为( C )。
    A. 1和1 B. 1和3 C. 1和2 D. 2和3

6.3.2 广义表的实现

介绍广义表的头尾链表存储表示以及子表分析法存储表示。
在这里插入图片描述

6.3.2.1 头尾链表存储表示

在这里插入图片描述

6.3.2.2 子表分析法

在这里插入图片描述

6.3.2.3 例题
  1. 广义表A=((a) , ((b,c),d)),画出头尾链表存储结构,求其长度和深度。

长度2,深度3。

  1. 已知广义表 LS=((a,b), (c,d,e,f), g), head 和 tail 分别是求表头和表尾,则tail( tail( head( tail(LS)))) 的运算结果是_____。(e , f)
  2. 广义表运算式HEAD(TAIL(((a,b,c),(x,y,z))))的结果是_______。(x,y,z)

6.3.3 广义表的递归操作

内容:介绍广义表的递归操作

  1. 递归函数

一个含直接或间接调用本函数语句的函数被称之为递归函数,它必须满足以下两个条件:
1)在每一次调用自己时,必须是(在某种意义上)更接近于解;
2)必须有一个终止处理或计算的准则。

  1. 用分治法设计递归函数

​ 对于一个输入规模为 n 的函数或问题,用某种方法把输入分割成k (1<k≤n)个子集,从而产生 l 个子问题,分别求解这 l 个问题,得出 l 个问题的子解,再用某种方法把它们组合成原来问题的解。若子问题还相当大,则可以反复使用分治法,直至最后所分得的子问题足够小,以至可以直接求解为止。
​ 在利用分治法求解时,所得子问题的类型常常和原问题相同,因而很自然地导致递归求解。

  1. 对广义表进行分解

广义表从结构上可以分解成
广义表 = 表头 + 表尾

或者
广义表 = 子表1 + 子表2 + ··· + 子表n
因此常利用分治法求解之。
算法设计中的关键问题是,如何将 l 个子问题的解组合成原问题的解。

  1. 求广义表的深度

将广义表分解成 n 个子表,分别(递归)求得每个子表的深度,
广义表的深度=Max {子表的深度} +1
可以直接求解的两种简单情况为:
空表的深度 = 1
原子的深度 = 0

/*
广义表的深度=Max {子表的深度} +1
空表的深度 = 1,    原子的深度 = 0
例:D=( A,B,C )=( ( ),(e),(a,(b,c,d)))
DEPTH(D)=1+MAX{ DEPTH(A),DEPTH(B),DEPTH(C)}
DEPTH(C)=1+MAX{ DEPTH(a),DEPTH((b,c,d))}=2
DEPTH(D)=1+MAX{ 1,1,2}=3
*/


typedef enum {ATOM, LIST} ElemTag;
          // ATOM==0:原子, LIST==1:子表
typedef struct GLNode {
   ElemTag  tag;   // 标志域
   union{
     AtomType  atom;      // 原子结点的数据域
     struct {struct GLNode *hp, *tp;} ptr;
   };
} *GList

int GlistDepth(Glist L) {
       // 返回指针L所指的广义表的深度
     if (!L) return 1; 
     if (L->tag == ATOM) return 0; 
     for (max=0, pp=L; pp; pp=pp->ptr.tp){
        dep = GlistDepth(pp->ptr.hp);
        if (dep > max) max = dep;
     }
     return max + 1;
  } // GlistDepth

    
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zyw2002

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

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

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

打赏作者

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

抵扣说明:

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

余额充值