联系
数组和广义表可看作一种扩展的线性数据结构,其特殊性在于数据元素的构成上。从组成线性表的元素角度看,数组是由具有某种结构的数据元素构成,广义表则是由单个元素或子表构成的。数组和广义表是线性表的推广。
数组
1.数组的定义
从逻辑结构上,数组可以看成是对一般线性表的扩充。一维数组即为线性表,而二维数组可以定义为其数据元素为一维数组(线性表)的线性表。以此类推,N维数组是数据元素为N-1维数组的线性表。
2.数组的顺序存储
原因: 一旦给定数组的维数n及各维长度bi(1≤i≤n),则该数组中元素的个数就是确定的,数组的基本操作不涉及数组结构的变化。因此对于数组而言,采用顺序存储表示比较适合。
方法: 内存储器的结构是一维的,对于一维数组可直接采用顺序存储,用一维的内存存储表示多维数组,就必须按照某种次序将数组中元素排成一个线性序列,然后将这个线性序列存放在一维的内存储器中,这就是数组的顺序存储结构。
分类: 按行序存储和按列序存储
二维数组Am✖n 以行为主的存储序列为:
a11,a12,…,a1n,a21,a22,…,a2n,…,am1,am2,…,amm
以列为主的存储序列为:
a11,a21,…,am1,a12,a22,…,am2,…,a1n,a2n,…,amn
计算:
(1)一维数组的地址计算
Loc(A[i])=Loc(A[1])+(i-1)✖size
(2)二维数组的地址计算
如果每个元素占size个存储单元:
Loc(A[i][j])=Loc(A[1][1])+((i-1)✖n+(j-1))✖size
如果每个元素占一个存储单元:
Loc(A[i][j])=Loc(A[1][1])+(i-1)✖n+(j-1)
(3)三维数组的地址计算
Loc(A[i][j][k])=Loc(A[1][1][1])+((i-1)✖m✖n+(j-1)✖n+(k-1))✖size
当 j1,j2,j3的下限分别为c1,c2,c3,上限分别为d1,d2,d3时
Loc(A[j1][j2][j3])=Loc(A[c1][c2][c3])+(j1-c1)✖((d2-c2+1)✖(d3-c3+1)+(j2-c2)✖(d3-c3+1)+(j3-c3))✖size
(4)n维数组的地址计算
Loc(A[j1][j2]…[jn])=Loc(A[c1][c2]…[cn])+Σ(i=1到n)ai✖(ji-ci)
其中,ai=size✖Π(k=i+1到n)(dk-ck+1),1≤i≤n
3.特殊矩阵的压缩存储
(1)规律分布的特殊矩阵
这类矩阵中元素分布的规律可以用数学公式来反映,通常利用这些规律将其压缩存储于一维数组中。
三角矩阵:
下三角矩阵中元素aij(i≥j),在一维数组中的存储单元的下标为:
Loc(A[i][j])=Loc(A[1][1])+(i✖(i-1)/2+j-1)
注意:对于对称矩阵,因其元素满足aij=aji,我们可以为每一对相等的元素分配一个存储空间,即只存下三角(或上三角)矩阵,从而将n²个元素压缩到n(n+1)/2个空间中
带状矩阵:
Loc(A[i][j])=Loc(A[1][1])+(3(i-1)-1+j-i+1)✖size
=Loc(A[1][1])+(2(i-1)+j-1)✖size
(2)稀疏矩阵
稀疏矩阵指的是大多数元素为0的矩阵。从直观上讲,当非零元素个数低于总元素的30%时,这样的矩阵为稀疏矩阵。
稀疏矩阵的三元组表表示法:
稀疏矩阵的三元组表类型定义
#define MAXSIZE 1000 /*非零元素的最大个数*/
typedef struct
{
int row,col; /*非零元素的行下标和列下标*/
ElementType e; /*非零元素的值*/
}Triple;
typedef struct
{
Triple data[MAXSIZE+1]; /*非零元素的三元组表,data[0]未用*/
int m,n,len; /*矩阵的行数,列数和非零元素的个数*/
}TSMatrix;
对于稀疏矩阵的压缩存储,采取只存储非零元素的方法。由于稀疏矩阵中非零元素aij的分布没有规律,因此,在存储非零元素值的同时,还必须存储非零元素在矩阵中所处的行号和列号,这就是稀疏矩阵的三元组表表示法。
稀疏矩阵的转置运算
第一步:将三元组表中的行列对换
第二步:若转置后的三元组表以行序为主序存放,则对转置后的三元组表按行标递增排列,反之亦然。
稀疏矩阵的链式存储结构:十字链表
十字链表的类型定义:
typedef struct OLNode
{
int row,col; /*非零元素的行下标和列下标*/
ElementType value;
struct OLNode *right,*down; /*非零元素所在行表,列表的后继链域*/
}OLNode;*OLink;
typedef struct
{
OLink *row_head,*col_head; /*行、列表的头指针向量*/
int m,n,len; /*稀疏矩阵的行数、列数、非零元素的个数*/
}CrossList;
为了避免大量移动元素,使用十字链表,能够灵活插入因运算而产生的新的非零元素,删除因运算而产生的新的非零元素,实现矩阵的各种运算。
除了(row,col,value)以外,还要添加两个链域:right用于链接同一行中的下一个非零元素,down用于链接同一列中的下一个非零元素。表示如下:
广义表
1.广义表的概念
广义表是n个数据元素(d1,d2,…dn)的有限序列,di既可以是单个元素,还可以是一个广义表,通常记作GL=(d1,d2,…dn)。其中d1是广义表表头,GL其余部分组成的表(d2,d3,…dn)称为广义表表尾。
2.广义表的存储结构
由于广义表中的数据元素既可以是单个元素,也可以是子表,因此对于广义表来说,难以用顺序存储结构来表示它,通常用链式存储结构来表示。
(1)广义表的头尾链表存储结构
任何一个非空的广义表都可以将其分解成表头和表尾两部分,繁殖,一对确定的表头和表尾可以唯一的确定一个广义表。因此:
表结点:由三个域构成,标志域、指向表头的指针域和指向表尾的指针域
元素结点:只需两个域,标志域和值域
广义表头尾链表存储结构类型定义
typedef enum {ATOM,LIST} ElemTag;/*ATOM=0,表示原子;LIST=1,表示子表*/
typedef struct GLNode
{
ElemTag tag; /*标志位tag用来区分原子结点和表结点*/
union
{
AtomType atom; /*原子结点的值域atom*/
struct {struct GLNode *hp,*tp;} htp;/*表结点的指针域htp,包括表头指针域hp和表尾指针域tp*/
}atom_htp; /*atom_htp是原子结点的值域atom和表结点的指针域htp的联合体域*/
}GLNode,*GList;
(2)广义表的同层结点链存储结构
这种结构中,表结点和元素结点都由三个域构成
表结点:标志域、指向表头的指针域和指向表尾的指针域
元素结点:标志域、值域和指向表尾的指针域
广义表的同层结点链存储结构类型定义如下
typedef enum {ATOM,LIST} ElemTag;/*ATOM=0,表示原子;LIST=1表示子表*/
typedef struct GLNode
{
ElemTag tag;
union
{
AtomType atom;
struct GLNode *hp; /*表头指针域*/
}atom_hp; /*atom_hp是原子结点的值域atom和表结点的表头指针域hp的联合体域*/
struct GLNode *tp; /*同层下一个结点的指针域*/
}GLNode,*GList;
3.广义表的操作
以头尾链表为例
(1)求广义表L的表头
GList Head(GList L)
{
if(L==NULL) /*空表无表头*/
return 0;
if(L->tag==ATOM) /*原子不是表*/
exit(0);
else
return(L->atom_htp.htp.hp);
}
(2)求广义表L的表尾
GList Tail(GList L)
{
if(L==NULL) /*空表无表尾*/
return(0);
if(L->tag==ATOM) /*原子不是表*/
exit(0);
else
return(L->atom_htp.htp.tp);
}
(3)求广义表L的长度
int Length(GList L)
{
int k=0;
GLNode *s;
if(L==NULL)
return(0);
if(L->tag==ATOM)
exit(0);
s=L;
while(s!=NULL)
{
k++;
s=s->atom_htp.htp.hp;
}
return(k);
}
(4)求广义表L的深度
int Depth(GList L)
{
int d,max;
GLNode *s;
if(L==NULL)
return(1);
if(L->tag==ATOM)
return(0);
s=L;
while(s!=NULL)
{
d=Depth(s->atom_htp.htp.hp);
if(d>max)
max=d;
s=s->atom_htp.htp.hp;
}
return(max+1); /*表的深度等于最深子表的深度加一*/
}
(5)求广义表L中原子数目
int CountAtom(GList L)
{
int n1,n2;
if(L==NULL)
return(0);
if(L->tag==ATOM)
return(1);
n1=CountAtom(L->atom_htp.htp.hp); /*求表头中的原子数目*/
n2=CountAtom(L->atom_htp.htp.tp); /*求表尾中的原子数目*/
return(n1+n2);
}
(6)复制广义表L
复制S表至T表
int CopyGList(GList S,GList *T)
{
if(S==NULL) /*复制空表*/
{
*T=NULL;
return(1);
}
*T=(GLNode*)malloc(sizeof(GLNode));
if(*T==NULL)
return(0);
(*T)->tag=S->tag;
if(S->tag==ATOM)
(*T)->atom=S->atom; /*复制单个原子*/
else
{
CopyGList(S->atom_htp.htp.hp,&((*T)->atom_htp.htp.hp));/*复制表头*/
CopyGList(S->atom_htp.htp.tp,&((*T)->atom_htp.htp.tp));/*复制表尾*/
}
return(1);
}