您的位置:首页 > 理论基础 > 数据结构算法

数据结构之数组和广义表

2016-03-24 09:58 531 查看
读自《数据结构(C语言版)》严蔚敏 吴伟民 清华大学出版社

当线性结构中的数据元素是原子类型时,即元素的值不再分解的(数据元素为int,char,…)

当数据元素是一个数据结构时,为非原子类型的。

C语言中,二维数组以行序为主序存储。

数组的顺序存储表示

#include <stdarg.h> //标准头文件,提供宏va_start/va_arg和va_end,
//用于存取变长参数表
#define MAX_ARRAY_DIM 8
typedef struct{
type *base;  //数组元素基址,由InitArray分配
int dim;  //数组维数
int *bounds;  //数组维界基址,由InitArray分配
int *constants;  //数组映像函数常量基址,由InitArray分配
}


矩阵的压缩存储,数值分析中经常出现一些阶数很高的矩阵,同时矩阵中有许多值相同的元素或者是零元素。有时为了节省存储空间,可以对这类矩阵进行压缩存储。所谓压缩存储是指,为多个值相同的元只分配一个存储空间;对零元不分配空间。

若值相同的元素或者零元素在矩阵中分布有一定规律,则我们称此类矩阵为特殊矩阵,反之,为稀疏矩阵

特殊矩阵

aij = aji 1<=i,j<=n 称为n阶对称矩阵。对于对称矩阵,可将n平方压缩存储到n(n+1)/2个元的空间中。

假设一维数组sa[n(n+1)/2]作为n阶对称矩阵A的存储结构,则sa[k]和矩阵元aij之间存在着一一对应的关系:

k = i(i-1)/2 + j - 1 当i >= j (下三角)

k = j(j-1)/2 + i - 1 当i < j (上三角)

三角矩阵,下(上)三角矩阵是指矩阵的上(下)三角(不包括对角线)中的元均为常数c或零的n阶矩阵。除了和对称矩阵一样,再加上一个存储常数c的存储空间即可

对角矩阵,在这种矩阵中,所有的非零元集中在以主对角线为中心的带状区域中。

m*n的矩阵,a = t/(m*n),t 为不为零的元素个数,a为稀疏因子,a<=0.05时称为稀疏矩阵。

稀疏矩阵可由表示非零元的三元组及其行列数唯一确定

稀疏矩阵的三元组顺序表存储表示

#define MAXSIZE 12500
typedef struct{
int i,j;  //该非零元的行下标和列下标
type e;
}Triple;

typedef struct{
Triple data[MAXSIZE+1];  //非零元三元组表,data[0]未用
int mu,nu,tu;  //矩阵的行数,列数,非零元个数
}TSMatrix;


m*n矩阵M转置矩阵T是一个n*m矩阵

转置操作:

1.将矩阵的行列值相互交换

2.将每个三元组中的i和j相互调换

3.重排三元组之间的次序便可实现矩阵的转置

两种处置转置的方法

Status TransposeSMatrix(TSMatrix M, TSMatrix &T){
T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
if(T.tu){
q = 1;
for(col = 1; col <= M.nu; ++col)
for(p = 1; p <= M.tu; ++p)
if(M.data[p].j == col){
T.data[q].i = M.data[p].j;
T.data[q].j = M.data[p].i;
T.data[q].e = M.data[[p].e;
++q;
}
}
return OK;
}


第二种就是先求得M的每一列中非零元的个数,进而求得每一列的第一个非零元在b.data中应有的位置,需要附设num和cpot两个向量。num[col]表示矩阵M中第col列中非零元的个数,cpot[col]指示M中第col列的第一个非零元在b.data中恰当位置

cpot[1] = 1;

cpot[col] = cpot[col - 1]+num[col - 1] 2<= col <= a.nu

行逻辑链接的顺序表

三元组顺序表又称有序的双下标法,它的特点是,非零元在表中按行序有序存储,因此便于进行依行顺序处理的矩阵运算。然而,如需要按行号存取某一行的非零元,则需要从头开始进行查找。

为了便于随机存取任意一行的非零元,则需知道每一行的第一个非零元在三元组表中的位置。我们借助指示行信息的辅助数组cpot

typedef struct{
Triple data[MAXSIZE + 1];  //非零元三元组
int rpos[MAXRC + 1];  //各行第一个非零元的位置表
int mu, nu, tu;   //矩阵的行数,列数和非零元个数
}RLSMatrix;


十字链表

广义表

广义表是线性表的推广,LS = (a1, a2, a3,…,an)

广义表的定义中,ai可以是单个元素,也可以是广义表,分别称为广义表LS的原子子表

习惯上,用大写字母表示广义表的名称,用小写字母表示原子。当广义表LS非空时,称第一个元素a1为LS的表头(Head),称其余元素组成的表(a2, a3, … , an)是LS的表尾(Tail)

列表的元素可以是子表,而子表的元素还可以是子表

列表可为其他列表所共享

列表可是一个递归的表,即列表也可以是其本身的一个子表

任何一个非空列表其表头可能是原子,也可能是列表,而其表尾必定为列表

广义表采用链式存储结构,需要用两种结构的结点:一种是表结点,用以表示列表;一种是原子结点,用以表示原子。

广义表的头尾链表存储表示

typedef enum{ATOM, LIST}ElemTag; //ATOM==0:原子,LIST == 1:子表
typedef struct GLNode{
ElemTag tag; //公共部分,用于区分原子结点还是表结点
union{  //原子结点和表结点的联合部分
AtomType atom; //atom是原子结点的值域,AotmType由用户定义
struct{struct GLNode *hp, *tp;}ptr;//ptr是表结点的指针域,ptr.hp和ptr.tp分别指向表头和表尾
};
}*GList;    //广义表类型


最高层的表结点个数即为列表的长度。

我比较习惯用下面这种结构

//广义表的扩展线性链表存储表示

typedef enum{ATOM, LIST}ElemTag; //ATOM==0:原子,LIST == 1:子表
typedef struct GLNode{
ElemTag tag; //公共部分,用于区分原子结点还是表结点
union{  //原子结点和表结点的联合部分
AtomType atom; //atom是原子结点的值域,AotmType由用户定义
struct GLNode *hp;//表结点的表头指针
};
struct GLNode *tp;   //相当于链表的next,指向下一个元素结点
}*GList;    //广义表类型


广义表的深度为广义表中括弧的重数,是广义表的一种量度。空表深度为1,原子深度为0

int GListDepth(GList 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;
}


复制广义表

一对确定的表头和表尾可唯一确定一个广义表,由此,复制一个广义表只要分别复制其表头和表尾,然后合成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: