第五章数组和广义表
5.1 数组的类型定义
5.2 数组的顺序表示和实现
5.3 稀疏矩阵的压缩存储
5.4 广义表的类型定义
5.5 广义表的表示方法
5.6 广义表操作的递归函数
1.1 数组的类型定义
//没有记全 ADT Array { 数据对象:D={aj1,j2…j |ji=0,…bi-1,i=1,2,…n} 数据关系:R={R1,R2,…Rn} Ri={<aj} 基本操作: } ADT Array |
基本操作:
InitArray(&A, n, bound1,…,boundn)
DestoryArray(&A)
Value(A, &e, index1,…,indexn)
Assign(&A, e, index1,…,indexn)
Value(A, &e, index1,…,indexn)
初始条件:A是n维数组,e为元素变量,随后是n个下标值。
操作结果:若各个下标不超界,则e赋值为所指定的A的元素值,并返回OK
Assign(&A, e, index1,…,indexn)
初始条件:A是n维数组,e为元素变量,随后是n个下标值。
操作结果:若下标不超界,则将e的值赋值给所指定的A元素,并返回OK.
5.2 数组的顺序表示和实现
类型特点:
1) 只有引用型操作,没有加工型操作
2) 数组是多维的结构,而存储空间是一个一维的结构
有两种顺序映象的方式
1) 以行序为主序(低下标优先)
2) 以列序为主序(高下标优先)
行序为主序
二维数组A中任意元素aij的存储位置
LOC[i,j] = LOC[0,0]+(b2*i+j)L 其中LOC[0,0]称为基地址或基址
推广到一般情况,可以得到n维数组数据元素的存储位置
5.3稀疏矩阵的压缩存储
假设m行n列的矩阵含t个非零元素,则称为稀疏因子,通常认为<=0.05的矩阵为稀疏矩阵。
以常规方法,即以二维数组表示高阶的稀疏矩阵时,产生的问题:
1) 零值元素占的空间很大
2) 计算中进行了很多和零值的运算
解决问题的原则
1) 尽可能少存或不存值为零值元素
2) 尽可能减少没有实际意义的运算
3) 运算方便;即:尽可能快地找到与下标值(i,j)对应的元素;尽可能快地找到同一行或同一列的非零值元。
1) 特殊矩阵的压缩存储
例如:三角矩阵 对角矩阵
2) 随机稀疏矩阵的压缩存储
随机矩阵中的非零元分布不规则
1))三元组顺序表
13_001 |
#define MAXSIZE 12500 typedef struct { int i,j; //该非零元的行下标和列下标 ElemType e; }Triple; //三元组类型 typedef union { Triple data[MAXSIZE+1]; int mu,nu,tu; }TSMatrix; //稀疏矩阵类型 |
求矩阵转置的操作:
用常规的二维数组表示时的算法
for(col = 1; col <= nu; ++ col)
for(row= 1; row <= mu; ++ row)
T[col][row]= M[row][col];
其时间复杂度为O(mu*nu)
14_001 |
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) { //转置矩阵元素 }//for }//if return OK; }//FastTransposeSMatrix |
三元组顺序表又称有序的双下标法,它的特点是,非零元在表中按行序存储,因此便于进行依行顺序处理的矩阵运算。然而,若需随机存取某一行中的非零元,则需从头开始进行查找。
二、行逻辑联接的顺序表
修改前述的稀疏矩阵的结构定义,增加一个数据成员rpos,其值在稀疏矩阵的初始化函数中确定。
14_002 |
#define MAXMN 500 typedef struct { Triple data[MAXSIZE+1]; int rops[MAXMN+1]; INT mu,nu,tu; }RLSMatrix; //行逻辑链接顺序表类型 |
矩阵乘法的经典算法:
for(i = 1; i <= m1; ++ i)
for(j= 1; j <= n2; ++ j)
{
Q[i][j]= 0;
for(k= 1; k <= n1; ++ k)
Q[i][j]+= M[i][k] * N[k][j];
}
其时间复杂度为:O(m1*n2*n1)
两个稀疏矩阵相乘(Q = M*N)
Q初始化
If Q是非零矩阵
{
//逐行求积
for(arow= 1; arow < M.mu; ++ arow)
{
//处理M的每一行
ctem[]= 0; //累加器清零
//计算Q中第arow行的积并存入ctemp[]中;
//将ctemp[]中非零元压缩存储到Q.data;
}//forarow
}//if
14_003 |
Status MultSMatrix(RLSMatrixM, RLMatrixN, RLSMartrix &Q) { if(M.nu != N.mu) { return ERROR; } Q.mu = M.mu; Q.nu = N.nu; Q.tu = 0; if(M.tu * N.tu != 0) { //Q是非零矩阵 for(arow = 1; arow <= M.mu; ++ arow) { //处理M的每一行 //... ctemp[] = 0; //当前行各元素累加器清零 Q.rpos[arow] = Q.tu + 1; for(p = M.rpos[arow]; p < M.rpos[arow+1]; ++ p) { //当前行中每一个非零元 brow = M.data[p].j; if(brow < N.nu) t = N.rpos[brow+1]; else for(q = N.rpos[brow]; q < t; ++ q) { ccol = N.data[q].j; //乘积元素在Q中列号 ctemp[ccol] += M.data[p].e * N.data[q].e; } //for q } //求得Q中第crow(=arow)行的非零元 for(ccol = 1; ccol <= Q.nu; ++ ccol) if(ctemp[ccol]) { if(++ Q.tu > MAXSIZE) return ERROR; Q.data[Q.tu] = {arow, ccol, ctemp[ccol]); }//if }//for arow }//if return OK; }//MultSMatrix |
分析上述算法的时间复杂度
累加器ctemp初始化的时间复杂度为O(M.mu<N.nu),求Q的所有非零元的时间复杂度为O(M.tu*N.tu/N.mu),进行压缩存储的时间复杂度为O(M.mu*N.nu),总的时间复杂度就是O(M.mu*N.nu+M.tu*N.tu/N.mu)。若M是m行n列的稀疏矩阵,N是n行p列的稀疏矩阵,则M中非零元的个数N中非零元的个数
相乘算法的时间复杂度就是,
1. 了解数组的两种存储表示方法,并掌握数组以行为主的存储结构中地址计算方法。
2. 掌握对特殊矩阵进行压缩存储时下标变换公式
3. 了解稀疏矩阵的两种压缩存储方法的特点和适用范围,领会以三元组表示稀疏矩阵时进行矩阵运算采用的处理方法。
第六章树
6.1树的类型定义
6.2二叉树的类型定义
6.3二叉树的存储结构
6.4 二叉树的遍历
6.5 线索二叉树
6.6 树和森林的表示方法
6.7 树和森林的遍历
6.8哈夫曼树与哈夫曼编码
6.1树的类型定义
数据对象D:
D是具有相同特性的数据元素的集合
数据关系R:
若D为空集,则称为空树;
否则:
(1)当D中存在唯一的称为根的数据元素root
(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,…,tm,其中每一棵子集本身又是一棵复合本定义的树,称为根root的子树。
基本术语
结点:数据元素+若干指向子树的分支
结点的度:分支的个数
树的度:树中所有结点的度数的最大值
叶子结点:度为零的结点
分支结点:度大于零的结点
从根到结点的路径:由从根到该结点所经分支和结点构成
孩子结点,双亲结点,兄弟结点,祖先结点,子孙结点
结点的层次:假设根结点的层次为1,第L层的结点的子树跟结点的层次为L+1
树的深度:树中叶子结点所在的最大层次
森林:是m(m>=0)棵互不相交的树的集合
任何一棵非空树是一个二元组
Tree=(root,F) 其中:root被称为根结点,F被称为子树森林
基本操作:
查找插入删除
查找:
Root(T);
Parent(T,cur_e)
LeftChild(T,cur_e)
RightSibling(T,cur_e)
TreeEmpty(T)
TreeDepth(T);
TraverseTree(T,visit());
Value(T,cur_e);
插入:
InitTree(&T);
CreateTree(&T,definition);
Assign(T,cur_e,value);
InsertChild(&T,&p,i,c);
删除:
ClearTree(&T);
DestoryTree(&T);
DeleteChild(&T,&p,i);
有向树:
1) 有确定的根;
2) 树根和子树根之间为有向关系
有序树和无序树的区别在于:
子树之间是否存在次序关系?
一般情况下讨论无序树
和线性结构的比较
线性结构 第一个数据元素(无前驱) 最后一个数据元素(无后继) 其它数据元素(一个前驱,一个后继) |
树结构 根结点(无前驱) 多个叶子结点(无后继) 树中其它结点(一个前驱,多个后继) |
6.2二叉树的类型定义
二叉树或为空树;或是由一个根结点加上两科分别称为左子树和右子树的、互不相交的二叉树组成。
二叉树的物种基本形态
2.4已知:A=(a1,a2,…,an)B=(b1,b2,…bm)均为顺序表,试编写一个比较A,B大小的算法
分析:1.算法的目标是分析两个的大小,则算法中不应当破坏原表;
1. 按题意,表的大小指的是“词典次序”,则不应当先比较两个表的长度
2. 算法中的几百呢操作为:同步比较两个表中相应的数据元素
假设int compare(SqList La, SqlList Lb)
这主要操作为:
if(La.elem[i] == Lb.elem[i])
i++;
else if(La.elem[i]<Lb.elem[i])
return-1;
else return 1;
循环条件:(i<=La.length) && (i<=Lb.Length)
循环结束时可能有三种情况
1.i>La.Length && i>Lb.Lengthreturn 0;
2.i<=La.Length && i>Lb.Lengthreturn 1;
3.i>La.Length && i<=Lb.Lengthreturn -1;