数组和广义表

来源:互联网 发布:可意下载 mac 破解版 编辑:程序博客网 时间:2024/06/05 15:32

5.1 数组的定义

数组:由一组类型相同、下标不同的变量构成。

特点:各个元素具有统一类型、下标 具有固定上界和下界、基本操作简单(初始化、销毁、修改、存取)

N维数组:n个下标,每个元素受到n个关系约束;一个n维数组可以看成是由若干个n-1维数组成的线性表。

5.2 数组的顺序存储

计算机的存储结构是一维的,而数组一般是多维的,那么就要进行一维化:事先约定按某种次序将数组元素排成一列序列,然后将这个线性表存入存储器中。例如:二维数组可以规定按行存储,也可以规定按列存储。C中采用行优先顺序。

利用一维线性存储的特性,可以计算数组元素的地址,必须知道的参数:①开始节点的存放地址即基地址②维数和每维的上下界③每个数组元素所占用的单元数。

n维数组任意元素的地址计算公式:

Loc(j1,j2,j3,…,jn)=Loc(0,0,..,0)+  其中cn=L,ci-1=bi*ci , 1<i≤n。

N维数组的顺序存储表示:

#define MAX_ARRAY_DIM 8 //假设最大维数为8

typedef struct{

     ElemType *base; //数组元素基址

  int dim;//数组维数

  int *bound;//数组各维长度信息保存区基址

  int *constants;//数组映像函数常量的基址

}Array;

数组的链式存储方式—用带行指针向量的单链表表示。

5.3 矩阵的压缩存储

为了节省数组元素的存储空间,所谓的压缩存储就是为多个值相同的元素只分配一个存储空间;对0元素不分配空间。若值相同的元素或0元素在矩阵中的分布有一定规律,则称此类矩阵为特殊矩阵;反之,成为稀疏矩阵(非0元素少)。

那么在稀疏矩阵中如何存储那些非0元素,稀疏矩阵的表示方法:

①  三元组:(i,j,aij

②  十字链表:

i

J

V

Down

right

    

 

 

 

down:同一列中下一非零元素的指针

right:同一行中下一非零元素的指针

每行非零元素链接成带表头结点的循环链表;每列非零元素也链接成带表头结点的循环链表。

③  三元组矩阵表:失去时机存储功能

④  带辅助向量的三元组表示。增加两个辅助向量。

记录每行非0元素个数,用NUM(i)表示;每行第一个非0元素在压缩后的三元组中的行号,用POS(i)表示。示例:

对于稀疏矩阵:
0  12 9  0  0  0

0  0   0 0  0  0

-3  0  0 0  14  0

0  0   24 0 0   0

0  18  0 0  0   0

15  0  0 -7  0  0

辅助向量表:

i

1

2

3

4

5

6

NUM(i)

2

0

2

1

1

2

POS(i)

1

3

3

5

6

7

POS(i)=POS(i-1)+NUM(i-1)

  三元组矩阵表:

i

j

v

6

6

8

1

2

12

1

3

9

3

1

-3

3

5

14

4

3

24

5

2

18

6

1

15

6

4

-7

 

 

稀疏矩阵的转置,实现方法:压缩转置和快速转置。(以三元组表的形式表示稀疏矩阵)

压缩转置:反复扫描原表序列,从j=1-n依次进行转置。

快速转置:生成矩阵三元组表的按列优先的辅助向量,然后实现快速转置。 该辅助矩阵中num行记录第col列的非零个数,第一个cpos默认为1,之后的cpos为前一项的col和num之和。

col

1

2

3

4

5

6

num[col]

2

2

2

1

1

0

cpos[col]

1

3

5

7

8

8

利用上面的辅助向量进行转置:遍历非零元素,通过元素的col列查找到相应的cpos[col]即其在转置矩阵中的位置为cpos[col],将该元素与三元矩阵相应的第cpos[col]交换后将矩阵表中的cpos[col]++和num[col]--。

两个算法的比较:记矩阵中列数为n,非零元素为t,m为总行数,则压缩转置时间复杂度为O(m*t),快速转置则为O(n+t)。快速转置增设辅助向量,空间换取时间。

5.4 广义表的定义

广义表:元素的值非原子类型,可以再分解,表中元素也可是一个线性表;所有数据元素仍然属于同一数据类型。

定义:广义表是线性表的推广,也称为列表。记为:LS=(a1,a2,…,an);其中表名为LS,表头为a1,表尾包括了从a2到an

约定:①用小写字母表示原子类型,用大写字母表示列表;②第一个元素是表头,而其余元素则组成表尾。

广义表中元素既可是原子类型,也可是列表;当每个元素都为原子且类型相同时,就是线性表。

特点:1.次序性:每个元素都有一个直接前驱和一个直接后继,首尾有点特殊性2.有长度,表中元素个数3.有深度,表中括号重数4.可递归,自己作为自己的字表5.可共享

 

5.5 广义表的存储结构

   通常用链式结构,每个元素用一个结点表示。

   原子结点:表示原子,可设2个域或三个域。例如(tag=0,value)或(tag=0,atom,tp),其中tp是指向表尾的指针域。

   表结点:表示列表,若表不空,可以分解为表头和表位,三个域:tag=1,表头指针(指向表头元素),表尾指针(指向表尾列表)。

5.6 总结

1. 数组可以视为一种广义线性表

2. 数组的存储有行/低地址优先和列/高地址优先两种不同的顺序

3. 对于稀疏矩阵,有较好的压缩存储和运算方法

4. 广义表是线性表的推广,也是一种线性结构

5. 任何一个非空表,表头可能是原子,也可能是列表,但是表尾一定是列表


⒈广义表的定义和性质

我们知道,线性表是由n个数据元素组成的有限序列。其中每个组成元素被限定为单元素,有时这种限制需要拓宽。例如,中国举办的某体育项目国际邀请赛,参赛队清单可采用如下的表示形式:

(俄罗斯,巴西,(国家,河北,四川),古巴,美国,(),日本)

在这个拓宽了的线性表中,韩国队应排在美国队的后面,但由于某种原因未参加,成为空表。国家队、河北队、四川队均作为东道主的参赛队参加,构成一个小的线性表,成为原线性表的一个数据项。这种拓宽了的线性表就是广义表。

广义表(Generalized Lists)是n(n≥0)个数据元素a1,a2,…,ai,…,an的有序序列,一般记作:

ls=(a1,a2,…,ai,…,an

其中:ls是广义表的名称,n是它的长度。每个ai(1≤i≤n)是ls的成员,它可以是单个元素,也可以是一个广义表,分别称为广义表ls的单元素和子表。当广义表ls非空时,称第一个元素a1为ls的表头(head),称其余元素组成的表(a2,…,ai,…,an)为ls的表尾(tail)。

显然,广义表的定义是递归的。

为书写清楚起见,通常用大写字母表示广义表,用小写字母表示单个数据元素,广义表用括号括起来,括号内的数据元素用逗号分隔开。下面是一些广义表的例子:

A =()

B =(e)

C =(a,(b,c,d))

D =(A,B,C)

E =(a,E)

F =(())

⒉广义表的性质

从上述广义表的定义和例子可以得到广义表的下列重要性质:

⑴广义表是一种多层次的数据结构。广义表的元素可以是单元素,也可以是子表,而子表的元素还可以是子表,…。

⑵广义表可以是递归的表。广义表的定义并没有限制元素的递归,即广义表也可以是其自身的子表。例如表E就是一个递归的表。

⑶广义表可以为其他表所共享。例如,表A、表B、表C是表D的共享子表。在D中可以不必列出子表的值,而用子表的名称来引用。

广义表的上述特性对于它的使用价值和应用效果起到了很大的作用。

广义表可以看成是线性表的推广,线性表是广义表的特例。广义表的结构相当灵活,在某种前提下,它可以兼容线性表、数组、树和有向图等各种常用的数据结构。

当二维数组的每行(或每列)作为子表处理时,二维数组即为一个广义表。

另外,树和有向图也可以用广义表来表示。

由于广义表不仅集中了线性表、数组、树和有向图等常见数据结构的特点,而且可有效地利用存储空间,因此在计算机的许多应用领域都有成功使用广义表的实例。

⒊广义表基本运算

广义表有两个重要的基本操作,即取头操作(Head)和取尾操作(Tail)。

根据广义表的表头、表尾的定义可知,对于任意一个非空的列表,其表头可能是单元素也可能是列表,而表尾必为列表。例如:

Head(B)= e Tail(B)=()

Head(C)= a Tail(C)=((b,c,d))

Head(D)= A Tail(D)=(B,C)

Head(E)= a Tail(E)=(E)

Head(F)=() Tail(F)=()

此外,在广义表上可以定义与线性表类似的一些操作,如建立、插入、删除、拆开、连接、复制、遍历等。

CreateLists(ls):根据广义表的书写形式创建一个广义表ls。

IsEmpty(ls):若广义表ls空,则返回True;否则返回False。

Length(ls):求广义表ls的长度。

Depth(ls):求广义表ls的深度。

Locate(ls,x):在广义表ls中查找数据元素x。

Merge(ls1,ls2):以ls1为头、ls2为尾建立广义表。

CopyGList(ls1,ls2):复制广义表,即按ls1建立广义表ls2。

Head(ls):返回广义表ls的头部。

Tail(ls):返回广义表的尾部。

……

5.4.2 广义表的存储

由于广义表中的数据元素可以具有不同的结构,因此难以用顺序的存储结构来表示。而链式的存储结构分配较为灵活,易于解决广义表的共享与递归问题,所以通常都采用链式的存储结构来存储广义表。在这种表示方式下,每个数据元素可用一个结点表示。

按结点形式的不同,广义表的链式存储结构又可以分为不同的两种存储方式。一种称为头尾表示法,另一种称为孩子兄弟表示法。

⒈头尾表示法

若广义表不空,则可分解成表头和表尾;反之,一对确定的表头和表尾可惟一地确定一个广义表。头尾表示法就是根据这一性质设计而成的一种存储方法。

由于广义表中的数据元素既可能是列表也可能是单元素,相应地在头尾表示法中结点的结构形式有两种:一种是表结点,用以表示列表;另一种是元素结点,用以表示单元素。在表结点中应该包括一个指向表头的指针和指向表尾的指针;而在元素结点中应该包括所表示单元素的元素值。为了区分这两类结点,在结点中还要设置一个标志域,如果标志为1,则表示该结点为表结点;如果标志为0,则表示该结点为元素结点。其形式定义说明如下:

typedef enum {ATOM, LIST} Elemtag; /*ATOM=0:单元素;LIST=1:子表*/

typedef struct GLNode {

Elemtag tag; /*标志域,用于区分元素结点和表结点*/

union { /*元素结点和表结点的联合部分*/

datatype data; /*data是元素结点的值域*/

struct {

struct GLNode *hp, *tp

}ptr; /*ptr是表结点的指针域,ptr.hp和ptr.tp分别*/

/*指向表头和表尾*/

};

}*GList; /*广义表类型*/


0 0
原创粉丝点击