数据结构与算法分析~笔记5数组与广义表

数组和广义表可以看做是线性表的扩展,即数组和广义表中的数据元素本身也是一种数据结构。数组中每个数据元素具有相同的结构,广义表中的数据元素则可以有不同的数据结构,两者都广泛应用于计算机的各个领域。

5.1 数组的基本概念

数组(Array)是由相同类型的一组数据元素组成的一个有限序列。其数据元素通常也称为数组元素。数组中的每个数据元素都有一个序号,称为下标(Index)。可以通过数组下标访问数据元素。
数据元素受n(n≥1)个线性关系的约束,每个数据元素在n个线性关系中的序号i1,i2,…,in称为该数据元素的下标,并称该数组为n维数组。当n为2时,该数组称为二维数组。例如,图中是一个m行,n列的二维数组A,其中任何一个数据元素有2个下标,一个为行号,另一个为列号。如aij表示第i行第j列的数据元素。
在这里插入图片描述
数组也是一种线性数据结构,它可以看成是线性表的一种扩充。一维数组可以看作是一个线性表,二维数组可以看作数据元素是一维数组(或线性表)的线性表,其中一行或一列就是一个一维数组的数据元素。例如图中的二维数组既可表示成一个行向量的线性表(A1,A2,…,Am称为行向量):
在这里插入图片描述

也可以表示成一个列向量的线性表(B1,B2,…,Bn称为列向量):
在这里插入图片描述
数组中的每个数据元素都和一组唯一的下标值对应。因此,数组是一种随机存取结构。一旦定义了一个数组,它的维数和每一维的长度就不能再改变。因此,数组的基本操作除了创建数组和销毁数组外,就只有对数据元素进行的存取和修改操作了。

5.2 数组的存储结构

一般不对数组进行插入和删除操作,也就是说,数组一旦建立,数据元素个数和数据元素间的关系就不再发生变化。因此,一般都是采用顺序存储的方法来表示数组。
根据存储方式的不同,顺序存储方法分为以下两类:
(1)行优先顺序存储:这是以行序为主序的存储方式。将数据元素按行排列,第i+1个行向量紧接在第i个行向量后面。以二维数组A为例,按行优先顺序存储的线性序列为:a11,a12,…,a1n,a21,a22,…,a2n,……am1,am2,…,amn。PASCAL、C/C++语言中,数组是按行优先顺序存储的。
(2)列优先顺序存储:这是以列序为主序的存储方式。将数据元素按列排列,第j+1个列向量紧接在第j个列向量之后,以二维数组A为例,按列优先顺序存储的线性序列为:a11,a21,…,am1,a12,a22,…,am2,……a1n,a2n,…,amn。
以上规则可以推广到多维数组的情况:行优先顺序可规定为先排最右的下标,从右到左,最后排最左的下标:列优先顺序与此相反,先排最左的下标,从左向右,最后排最右的下标。
按上述两种方式存储的数组,只要知道开始结点的存放地址(即基地址)、维数和每维的上、下界,以及每个数据元素所占用的存储单元数,就可以将数据元素的存放地址表示为其下标的线性函数。因此,数组中的任一数据元素可以在相同的时间内存取。

5.3 矩阵的压缩存储

在矩阵中,若非零数据元素呈某种规律分布或矩阵中出现大量零数据元素,如果仍采用常规的矩阵存储方法,将会占用许多空间去存储重复的非零数据元素或零数据元素,对于高阶矩阵来说,这样会造成极大的浪费。因此,为了节省存储空间,可以对这类矩阵进行压缩存储。压缩存储的原则是:
(1)为多个值相同的非零数据元素分配一个存储空间;
(2)不为零数据元素分配存储空间。
假如值相同的数据元素或者零数据元素在矩阵中的分布有一定规律,则称此类矩阵为特殊矩阵(Special Matrix);假如矩阵中有许多零数据元素(一般根据稀疏因子的值判定零数据元素是否较多),则称此类矩阵为稀疏矩阵(Sparse Maxtrix)
特殊矩阵指非零数据元素或零数据元素的分布有一定规律的矩阵。常见的特殊矩阵有对称矩阵、对角矩阵等,它们都是方阵,即行数和列数相同。

  1. 对称矩阵的压缩存储
    在一个n阶方阵A中,若数据元素满足下述性质:aij=aji(i≥1,j≤n)则称A为n阶对称矩阵,如图所示。
    在这里插入图片描述
    对称矩阵中的数据元素关于主对角线对称,因此只需存储矩阵中上三角aij(i≤j)或下三角aij(i≥j)中的数据元素即可。这样,原来需要n×n个存储单元,现在只需要(1+2+…+n)=n×(n+1)/2个存储单元,节约了近一半的存储空间,当n较大时,节约出的存储空间是可观的。
    不失一般性,对称矩阵采用行优先顺序存储下三角中的数据元素,如图所示。上述下三角中的数据元素可用一个容量是n×(n+1)/2的一维数组存储。对于下三角中的任意数据元素aij(i≥j),aij在一维数组中的下标k与i、j的关系为:k=i×(i-1)/2+j。
    在这里插入图片描述
    因此,对称矩阵若采用上述压缩存储方式,则矩阵中任一数据元素aij与它在一维数组中的存储位置k之间存在如下的对应关系:
    在这里插入图片描述
    其中,k=0,1,2,…,((n+1)n/2)-1,1+2+3+(i-1)=(i-1)i/2,(i-1)i/2+j=k+1 i≥j且i,j≥1.
  2. 对角矩阵的压缩存储
    对角矩阵是所有的非零数据元素都集中在以主对角线为中心的带状区域中的矩阵,即除了主对角线上和主对角线相邻两侧的若干条对角线上的数据元素之外,其余所有数据元素皆为零数据元素。
    对于一个m×n的对角矩阵A,若其主对角线上存有非零元的个数为w,则一种压缩存储方法是将其压缩到一个m×w的二维数组B中,如图所示。A中任一非零元a[i][j]压缩存储后,对应B中的数据元素b[s]t,则两个数据元素的地址对应关系为:
    在这里插入图片描述
    另一种压缩存储方法是将其存储到一个一维数组C中,按行优先顺序存储非零数据元素,根据压缩规律,找到相应的映射函数。如下图所示:
    在这里插入图片描述
  3. 稀疏矩阵
    设矩阵A中有s个非零数据元素,若s远远小于矩阵数据元素的总数(即s<<m×n),则称A为稀疏矩阵(Sparse Matrix)。至于远远小于如何界定,目前还没有一个确切的定义。假设在m×n阶矩阵中有s个非零数据元素,令t=s/(m×n),称t为矩阵的稀疏因子。通常认为t<<0.05时,称之为稀疏矩阵。
    除了要存储非零数据元素的数据值,还必须同时记下它所在的行和列的位置(i,j)。本书中,在三元组顺序表中序号i、j的值均从1开始。所以,一个三元组(i,j,aij)唯一确定了矩阵A的一个非零数据元素。由此,稀疏矩阵可由表示非零元的三元组及其行列数唯一确定。若把稀疏矩阵的三元组线性表按顺序存储结构存储,则称为稀疏矩阵的三元组顺序表
    稀疏矩阵A对应的三元组顺序表如下图所示:
    在这里插入图片描述

5.4 广义表的基本概念

广义表是一种特殊的结构,它兼有线性表、树、图等结构的特点。不同于线性表,广义表的数据元素不仅可以为基本数据类型,而且还可以为广义表。从基本数据元素的角度来看,广义表的数据元素之间已经不再是单纯的线性关系,而且还存在层次关系。
广义表(Generalized Lists)是n(n≥0)个数据元素的有限序列,一般记作:LS=(a1,a2…,an)其中,LS是广义表的名称,ai(1≤i≤n)是LS的直接数据元素(也称成员),它可以是单个的数据元素,也可以是一个广义表,它们分别称为LS的单数据元素(或原子)和子表。当广义表LS非空时,称第一个数据元素为LS的表头(Head);称广义表LS中除去表头后其余数据元素组成的广义表为LS的表尾(Tail)。广义表LS中的直接数据元素的个数称为LS的长度;广义表LS中括号的最大嵌套层数称为LS的深度
广义表的例子如下:
(1)A=():表A是一个空表,其长度为零。
(2)B=(e):表B只有一个原子e,B的长度为1。
(3)C=(a,(b,c,d)):表C的长度为2,包含的两个直接数据元素分别为原子a和子表(b,c,d)。(4)D=(A,B,C):表D的长度为3,包含的3个直接数据元素都是广义表。显然,将子表的值代入后,则有D=((),(e),(a,(b,c,d)))。
(5)E=(a,E):表E是一个递归的表,它的长度为2,E相当于一个无限的广义表E=(a,(a,(a,(a,…))))。
需要强调的是:广义表()和广义表(())是不同的,前者为空表,长度为0;后者长度为1,可分解得到表头和表尾均为空的子表()。
从广义表的定义可以看出广义表有如下性质:
(1)广义线性:对任意广义表,若不考虑其数据元素的内部结构,则它是一个线性表,它的直接数据元素之间是线性关系。
(2)数据元素复合性:广义表中的数据元素分两种:单数据元素和子表。因此广义表中数据元素的类型不统一。一个子表,在某一层次上被当作数据元素,但就它本身的结构而言,也是广义表。
(3)数据元素递归性:广义表是递归的。广义表的定义并没有限制数据元素的递归,即广义表可以是其自身的子表。这种递归性使得广义表具有较强的表达能力。
(4)数据元素共享性:广义表以及广义表的数据元素可以被其他广义表共享。例如上述广义表示例中,表A、表B、表C是表D的子表。
广义表有两个重要的基本操作,取表头和取表尾,通过取表头和取表尾操作,可以按递归方法处理广义表,也可以实现一般的访问。在广义表上可以定义与线性表类似的一些操作,如插入、删除、遍历等。
由于广义表中的数据元素的类型不统一,因此难以采用顺序存储结构。而链式存储结构较为灵活,易于解决广义表的共享与递归问题,所以通常采用链式存储结构来存储广义表。若广义表不空,则可分解为表头和表尾;反之,一对确定的表头和表尾可唯一地确定一个广义表。根据这一性质可采用头尾表示法(Head Tail Express)来存储广义表。
广义表中的数据元素即可以是广义表也可以是单数据元素,相应的在头尾表示法中链表的结点结构也有两种:一种是表结点,用以存储广义表;另一种是数据元素结点,用以存储单数据元素。为了区分这两类结点,在结点中还要设置一个标志域,如果标志为1,则表示该结点为表结点;如果标志为0,则表示该结点为数据元素结点。下图所示便是广义表头尾表示法的结点结构:
在这里插入图片描述
tag:区分表结点和数据元素结点的标志。hp:指向表头结点的指针。tp:指向表尾结点的指针。data:存储数据元素自身的信息。
对于广义表而言,递归在一些算法的实现上有着较好的效果,如求广义表深度、复制广义表以及建立广义表的存储结构等。

  1. 求广义表的深度
    非空广义表的深度定义为广义表中括号嵌套的最大层数,空表的深度为1。设非空广义表LS=(a1,a2,…,an),其中ai或是单数据元素,或是子表。这样,求LS的深度可以分解为n个子问题,每个子问题为求数据元素ai的深度。若ai是单数据元素,其深度为0;若ai是子表,则对ai继续分解。最后LS的深度为ai中深度的最大值加1。
  2. 复制广义表
    任何一个非空广义表均可分解成表头和表尾。反之,一对确定的表头和表尾可唯一确定一个广义表。因此复制一个广义表只要分别复制其表头和表尾,然后再重新合成即可。
  3. 建立广义表的存储结构
    对广义表进行的操作,可以有两种分析方法:一种是把广义表分解成表头和表尾两部分,即头尾表示法;另一种是把广义表看成是含有S个并列子表(其中原子也看作子表)的表。在建立广义表的存储结构时,上述两种方法均可。
    对于第一种分析方法,可以把广义表看做是一个字符串S,则当S为非空串时广义表为空。此时可通过广义表抽象数据类型定义中的GetHead()和GetTail()两个函数建立广义表的链式存储结构。
    而对于第二种分析方法,广义表字符串S可能有两种情况:(1)S=()(带括号的空白串)。(2)S=(a1,a2,…,an),其中ai=(i=1,2,…,n)是S的子串。
    下列建立广义表链式存储结构的递归定义:
    基本项:当S为空表串时,置空广义表;当S为单字符串时,创建原子结点的子表。
    归纳项:假设sub为S中脱去最外层括号的字串,记为‘s1,s2,…,sn’,其中si=(i=1,2,…,n)为非空字符串。对每个si建立一个表结点,并令其头结点指针为si建立的子表的头指针,除最后建立的表结点的尾指针为NULL外,其余表结点的尾指针均指向在它之后建立的表结点。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值