版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
DATASTRUCTUREDATASTRUCTUREDataStructureBYPENGBO
数组和广义表是两种广义线性表。在表、栈、队列及串的结构中,其元素都是原子类型。而数组和广义表可以看成是线性表在下述含义上的扩展:表中的元素本身也是一个数据结构。第5章主要内容数组和广义表第章5《数据结构–基于C语言的描述》(第2版)-清华大学出版社DATASTRUCTUREDATASTRUCTUREDataStructureBYPENGBO第5章主要内容数组和广义表第章55.1
数组5.2
矩阵的压缩存储5.3
广义表《数据结构–基于C语言的描述》(第2版)-清华大学出版社5.1数组
从逻辑结构上看,多维数组是一维数组的扩充;但是从存储结构上看,一维数组是多维数组的特例。在程序设计语言中,重点是数组的使用;而在数据结构中,重点是数组的内部实现,即数组的存储结构与寻址方法。5.1数组5.1.1数组的类型定义●数组的定义
数组是由下标与同类型值组成的数偶的有序集合。
由下标所决定的位置之间关系可以看成是一种有序的线性关系,因此数组是有限个同类型元素组成的有序序列。从这个角度看,数组是线性表的推广,其逻辑结构实际上是一种线性结构。
一维数组是一个线性表,其每个元素是该结构中不可分割的最小单位;二维数组是一个线性表,其每个元素是一个线性表;n(n>1)维数组是一个线性表,其每个元素是一个n-1维的数组,且具有相同的上限和下限。
5.1数组5.1.1数组的类型定义●数组的定义Am×n=((a00a01…a0,n-1),(a10a11…a1,n-1),…,(am-1,0am-1,1…am-1,n-1))行向量是一个线性表一个二维数组列向量是一个线性表5.1数组5.1.1数组的类型定义●数组的抽象数据类型ADTArray{Data:
j=0,…,bi-1,i=1,2,…,n
D={aj1j2…jn|aj1j2…jn
ElemSet,
其中:n(n>0)是数组维数,bi是数组第i维长度,ji是数组元素第i维下标}
Relation:
R={R1,R2,…,Rn} Ri={<aj1j2…jk…jn,aj1j2…jk+1…jn>|0≤jk≤bk-1,1≤k≤n
且k
i,0≤ji≤bi-2,aj1j2…jk…jn,aj1j2…jk+1…jn
D,i=2,…,n}
Operation:
…… ValueArray(A,&e,index1,…,indexn);
初始条件:n维数组A已经存在。 操作结果:若给定的各下标值index1,…,indexn不超界,则用e返回给定下标值所指定
的A的元素值。
……}ADTArray数组一旦被定义,其维数和维界就不再改变。因此,除了结构初始化和销毁外,数组运算通常只有两种。(1)存取:给定一组下标,存取相应的数组元素;
(2)修改:给定一组下标,修改相应的数组元素值。
5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的定义
数组中的元素按照逻辑次序依次存放在一组地址连续的存储单元里的方式称为数组顺序表。5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的定义
一维数组的顺序存储
一维数组(a0,a1,...,an-1)由n个元素组成,假设每个元素占L个存储单元,数组任一元素ai
的存储地址如下:LOC(ai)=LOC(a0)+i
L
多维数组是多维结构,采用顺序存储结构的数组需要将多维结构映射到一维结构。常用的映射方法有两种:按行优先和按列优先。
5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的定义
二维数组的顺序存储
按行优先存储方式:先行后列。假设每个元素占L个存储单元,数组任一元素aij
的存储地址如下:
LOC(aij)=LOC(a00)+(b2
i+j)
L5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的定义
二维数组的顺序存储
按列优先存储方式:先列后行。假设每个元素占L个存储单元,数组任一元素aij
的存储地址如下:
LOC(aij)=LOC(a00)+(b1
j+i)
L5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的定义
多维数组的顺序存储
设n维数组A第k维(1≤k≤n)的下标范围是[0,bk-1],如果每个元素占L个存储单元且从地址A开始依次分配数组中各元素,则数组A中下标为j1、j2、…、jn
的元素的按行优先存储地址如下:可以将上式缩写成如下形式:
其中:cn=L,ci-1=bi
ci, 1<i≤n。n维数组的映像函数。一旦确定了数组各维长度,ci
就是常数,因此,数组元素的存储地址是其下标的线性函数。数组中的任一元素可以在相同的时间内存取,即数组顺序表是一个随机存取结构。5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的类型定义//数组顺序表的存储表示#include<stdarg.h> //标准头文件
//提供宏va_start、va_arg和va_end
//用于存取变长参数表#defineMAX_ARRAY_DIM8 //假设数组维数的最大值为8typedefstruct{ElemType *base; //数组元素基址int dim; //数组维数int *bounds; //数组维界基址int *constants; //数组映象函数常量基址}Array;5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的类型定义//二维数组顺序表的存储表示typedefstruct{ElemType *base; //存储数组元素的基地址int b1,b2; //二维数组第一维和第二维的维界}Array;5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的操作实现
初始化操作算法5-1voidInitArray(Array&A,intm,intn){//构造一个维界分别是m和n的空二维数组AA.base=newElemType[n*m];if(!A.base)Error("Overflow!");A.b1=m;A.b2=n;}//InitArray5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的操作实现
销毁操作算法5-2voidDestroyArray(Array&A){//释放数组顺序表A所占用的存储空间delete[]A.base;A.b1=0;A.b2=0;}//DestroyArray5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的操作实现
取值操作算法5-3voidValueArray(ArrayA,ElemType&e,inti,intj){//若下标i和j合理,则用e返回其按行优先存储数组A的元素值;否则给出信息并退出运行//下标i和j的合理值为i∈[0,b1-1]和j∈[0,b2-1]if((0<=i)&&(i<A.b1)&&(0<=j)&&(j<A.b2)){off=A.b2*i+j;e=A.base[off];}elseError("SuffixError!");}//ValueArray5.1数组5.1.2数组的顺序存储表示及操作实现●数组顺序表的操作实现
赋值操作算法5-4voidAssignArray(Array&A,ElemTypee,inti,intj){//若下标i和j合理,则将e赋给其按行优先存储的数组A;否则给出相应信息并退出运行//下标i和j的合理值为i∈[0,b1-1]和j∈[0,b2-1]if((0<=i)&&(i<A.b1)&&(0<=j)&&(j<A.b2)){off=A.b2*i+j;A.base[off]=e;}elseError("SuffixError!");}//AssignArray5.1数组5.1.2数组的顺序存储表示及操作实现例5-1
二维数组A的每个元素由6个字符组成的串,行下标的范围是[0,8],列下标的范围是[0,9],试问:
(1)存放二维数组A至少需要多少个字节?
(2)如果A按行优先方式存储,则元素a85的起始地址与当A按列优先方式存储时的哪一个元素的起始地址一致。解答(1):
(1)因为二维数组A为9行10列,共有90个元素,所以存放A至少需要90
6=540个存储单元。5.1数组5.1.2数组的顺序存储表示及操作实现例5-1
二维数组A的每个元素由6个字符组成的串,行下标的范围是[0,8],列下标的范围是[0,9],试问:
(1)存放二维数组A至少需要多少个字节?
(2)如果A按行优先方式存储,则元素a85的起始地址与当A按列优先方式存储时的哪一个元素的起始地址一致。解答(2):
按行优先存储:LOC(a85)=LOC(a00)+(8
10+5)6=LOC(a00)+85;按列优先存储:LOC(aij)=LOC(a00)+(9
j+i)
6;
因为LOC(a85)=LOC(aij),则有9
j+i=85;解此不定方程,且限制:0≤i≤8,
0≤j≤9。得到i=4,j=9;所以当A按行优先方式存储时元素a85的起始地址与当A按列优先方式存储时元素a49的起始地址一致。5.2矩阵的压缩存储
在数值分析中经常出现阶数很高的矩阵,且在矩阵中有许多相同的元素或零元素。为了节省存储空间,可以对这类矩阵进行压缩存储。即:为多个值相同的元只分配一个存储空间,且对零元不分配空间。
5.2矩阵的压缩存储
非零元素或零元素分布具有一定规律的矩阵称为特殊矩阵。常见特殊矩阵有三种:对称矩阵、三角矩阵和对角矩阵。5.2.1特殊矩阵的压缩存储●对称矩阵
在一个n阶方阵中,如果元素满足性质:aij=aji(0≤i,j≤n-1),则称该矩阵为n阶对称矩阵。
例如:矩阵A是一个5阶对称矩阵。
对称矩阵关于主对角线对称,因此只需存储下(上)三角部分即可,需要n
(n+1)/2个存储单元,节约了大约一半的存储单元。5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●对称矩阵
存储方式对称矩阵按行优先存储对称矩阵的压缩存储SA[k]
按a00,a10,a11,…,an-1,0,an-1,1,…,an-1,n-1的次序依次存放在一维数组SA[0..n(n+1)/2-1]中。5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●对称矩阵
aij和SA[k]之间的对应关系
按行优先顺序存放对称矩阵中元素aij
时,aij
元素前面有i行,一共有元素个数:1+2+…+i=i
(i+1)/2。
在第i行上,aij
之前恰有j个元素。因此aij
之前共有i
(i+1)/2+j=k个元素。
●
如果i≥j,则k=i
(i+1)/2+j,且0≤k<n(n+1)/2;
●如果i<j,则k=j
(j+1)/2+i,且0≤k<n(n+1)/2。
●令I=max(i,j),J=min(i,j),则对称矩阵在一维数组SA中的下标k与i、j的对应关系可以统一为:
k=I
(I+1)/2+J,0≤k<n(n+1)/25.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●对称矩阵
对称矩阵的地址计算SA
例如:对称矩阵A中的元素a21
和a12
均存储在SA[4]中,因为k=I
(I+1)/2+J=2
(2+1)/2+1=4LOC(aij)=LOC(SA[k])
=LOC(SA[0]+k)
L
=LOC(SA[0]+[I
(I+1)/2+J])
L5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●三角矩阵上三角矩阵下三角矩阵
下三角或上三角(不包括对角线)中的元素均为常数c或零的n阶矩阵称为上三角矩阵或下三角矩阵。
例如:矩阵B是一个上三角矩阵;矩阵C是一个下三角矩阵。
三角矩阵按主对角线划分,因此只需存储下(上)三角部分和常数c,需要n
(n+1)/2+1个存储单元,节约了大约一半的存储单元。5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●三角矩阵
存储方式三角矩阵按行优先存储c三角矩阵的压缩存储SA[k]
按a00,a10,a11,…,an-1,0,an-1,1,…,an-1,n-1的次序依次存放在一维数组SA[0..n(n+1)/2-1]中,c存放在SA[n(n+1)/2]中。5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●三角矩阵
aij和SA[k]之间的对应关系
按行优先顺序存放上三角矩阵中元素aij
时,aij
前面有i行,一共有元素个数:(n-0)+(n-1)+(n-2)+…+(n-i+1)=i
(2n-i+1)/2。
在第i行上,aij
之前恰有j-i个元素(即aii,ai,i+l,…,ai,j-1)
,因此aij
之前共有i
(2n-i+1)/2+j-i=k个元素。
上三角矩阵在一维数组SA中的下标k与i、j的对应关系为:
i(2n-i)/2+j-i i≤jn(n+1)/2 i>jk=上三角矩阵5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●三角矩阵
aij和SA[k]之间的对应关系
按行优先顺序存放下三角矩阵中元素aij
时,aij
前面有i行,一共有元素个数:1+2+…+i=i
(i+1)/2。
在第i行上,aij
之前恰有j个元素,因此aij
之前共有i
(i+1)/2+j=k个元素。
下三角矩阵在一维数组SA中的下标k与i、j的对应关系为:
i(i+1)/2+j i≥jn(n+1)/2 i<jk=下三角矩阵5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●三角矩阵
三角矩阵的地址计算
上三角矩阵的下标变换公式如下:
LOC(aij)=LOC(SA[k])=LOC(SA[0])+k
L
=LOC(SA[0])+[i
(2n-i+1)/2+j-i]
L
下三角矩阵的下标变换公式如下:
LOC(aij)=LOC(SA[k])=LOC(SA[0])+k
L
=LOC(SA[0])+[i
(i+1)/2+j]
L
上(下)三角矩阵重复元素的下标变换公式如下:
LOC(aij)=LOC(SA[k])=LOC(SA[0])+k
L =LOC(SA[0])+[n
(n+1)/2]
L5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●对角矩阵
由此可知,一个w条对角线矩阵(w为奇数)A满足:如果|i-j|>(w-1)/2,则元素aij=0。
在一个n阶方阵中,所有非零元素都集中在以主对角线为中心的带状区域中,除了主对角线和其上下方若干条对角线的元素外,所有其他元素都为零,则称该矩阵为对角矩阵,也称为带状矩阵。
例如:矩阵D是一个3对角矩阵。
3对角矩阵5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●对角矩阵
存储方式3对角矩阵按对角线顺序的压缩存储3对角矩阵5
3矩阵
(1)将n阶w对角矩阵转换为一个n行w列n
w矩阵,在该矩阵中共有(w
n-(w2-1)/4)个非零元素及(w2-1)/4个零元素;(2)转换后的n行w列矩阵中所有元素按行存储到一维数组SA[w
n]中。
aij和SA[k]之间的对应关系
对角矩阵转换为一个n
w矩阵中的元素ats
映射关系为:
该n
w矩阵中元素ats
在一维数组SA中的下标k与t、s的关系为:k=w
t+s
比如,在3对角矩阵中:5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●对角矩阵
对角矩阵的地址计算
例如:一个5阶的3对角矩阵A
中的元素a22
存储在SA[7]中,因为t=i=2,s=j-i+(w-1)/2=1k=w
t+s=3
2+1=7k=w
t+s LOC(aij)=LOC(SA[k]) =LOC(SA[0])+k
L
=LOC(SA[0])+[w
t+s]
L
=LOC(SA[0])+[w
i+(j-i+(w-1)/2)]
L5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●对角矩阵5.2矩阵的压缩存储5.2.1特殊矩阵的压缩存储●对角矩阵
按行优先顺序的压缩存储
按a00,a01,a10,a11,a12,…,a43,a44
的次序依次存放在一维数组SA[0..12]。3对角矩阵按行优先存储3对角矩阵按行优先顺序的压缩存储
对n阶3对角矩阵,以主对角线为中心带状区域中共有(3
n-2)个非零元素,将所有元素按行存储到一维数组SA[3
n-2]中。
5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储假设在m
n矩阵中,如果有t个非零元素,令
=t/(m
n),则称
为该矩阵的稀疏因子。通常认为
≤0.05的矩阵为稀疏矩阵。
15阶矩阵3个非零元,
=3/(15
15)=0.013。5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●稀疏矩阵的抽象数据类型ADTSparseMatrix{Data:
D={ai,j|ai,j
ElemSet,i=0,1,…,m-1,n≥0;j=0,1,…,n-1,m≥0 m和n分别称为矩阵的行数和列数}
Relation:
R={Row,Col} Row={<ai,j,ai,j+1>|0≤i≤m-1,0≤j≤n-2}Col={<ai,j,ai+1,j>|0≤i≤m-2,0≤j≤n-1}
Operation:
…… AddSMatrix(M,N,&Q);
初始条件:稀疏矩阵M与N已经存在,且行数与列数对应相等。 操作结果:用Q返回M和N的和。
…… TransposeSMatrix(M,&T);
初始条件:稀疏矩阵M已经存在。 操作结果:用T返回M的转置矩阵。}ADTSparseMatrix5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●稀疏矩阵的抽象数据类型
除了存储非零元素值aij
之外还同时记下其所在行和列的位置(i,j),这样组成了一个三元组(i,j,aij)。
将稀疏矩阵非零元素对应的三元组所构成的集合,按行优先顺序排列成一个线性表,称为三元组表。
稀疏矩阵M的三元组表为:((0,2,-9),(0,4,5),(1,0,-7),(1,2,7),(3,1,8),(4,2,9))。稀疏矩阵可以由表示非零元素的三元组及其行列数唯一确定。
三元组表5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
三元组顺序表的定义
将表示稀疏矩阵非零元素的三元组按行优先顺序排列,并依次存放在一组地址连续的存储单元里的方式称为三元组顺序表。
例如:稀疏矩阵M及其三元组顺序表。5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
三元组顺序表的类型定义#defineTriList_Size1000 //三元组表大小,可根据实际需要而定typedefstruct{ //三元组int row,col; //非零元的行下标和列下标
ElemType e; //非零元的元素值}Triple;typedefstruct{ //三元组顺序表
Triple data[TriList_Size+1]; //非零元三元组表,data[0]未用
int mu,nu,tu; //矩阵的总行数、总列数和总非零个数}TSMatrix;
转置操作5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
(1)将稀疏矩阵M中非零元素的行、列值相互调换,即将M.data[]中每个元素的row和col相互调换;
(2)因为三元组顺序表中元素按行优先顺序排列,T.data[]中元素顺序和M.data[]中元素顺序不同,所以需排三元组顺序表中元素次序。(关键)完成两点常用处理方法直接取-顺序存顺序取-直接存5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
直接取–顺序存:按需点菜
基于T.data[]按行优先顺序在M.data[]
中依次扫描第0列、…、第M.nu-1列的三元组,从中“找出”元素进行“行列互换”后顺序插入到T.data[]中。这种方法称为“直接取-顺序存”,也称“按需点菜”法。设计5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
直接取–顺序存voidTransSMatrix_TSM(TSMatrixM,TSMatrix&T){//用T返回三元组顺序表M的转置矩阵InitSMatrix_TSM(T); //初始化三元组顺序表TT.mu=M.nu; //设置T的行数T.nu=M.mu; //设置T的列数T.tu=M.tu; //设置T的非零元素个数算法5-5if(T.tu){pt=1; //pt为T.data的下标,初始为1for(i=0;i<M.nu;++i)for(pm=1;pm<=M.tu;++pm) //pm为M.data的下标,初始为1if(M.data[pm].col==i){ //进行转置T.data[pt].row=M.data[pm].col;T.data[pt].col=M.data[pm].row;T.data[pt].e=M.data[pm].e;++pt;}}}//TransSMatrix_TSM5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
直接取–顺序存算法5-55.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
直接取–顺序存:按需点菜分析①问题规模:矩阵M列数M.nu(nu)和非零元素个数M.tu(tu);②基本操作:行列互换;③时间分析:基本操作的执行次数主要依赖于嵌套的两个for循环,因此算法的时间复杂度为O(nu
tu)。5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作
对M.data[]进行一次扫描,将所有非零元素的三元组在T.data[]中“一次到位”。这种方法称为“顺序取-直接存”,也称“按位就座”法。●
顺序取–直接存问题
如何确定当前从三元组表M.data中取出的三元组元素在三元组表T.data中位置。
5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
顺序取–直接存
M.data中第0列的第一个非零元存储在T.data下标为1位置上,该列中其他非零元则应该存放在T.data中后面连续位置上;
M.data中第1列的第一个非零元在T.data位置等于其第0列第一个非零元在T.data位置加上其第0列非零元数;……以此类推。问题
如何确定当前从三元组表M.data中取出的三元组元素在三元组表T.data中位置。
5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
顺序取–直接存
引入两个数组num[]和cpot[]作为辅助的数据结构解决上述问题。
解决只要能够提前知道M.data中每一列上第一个非零元在T.dada中位置及可以实现“一次到位”。(1)先求M的每一列中非零元个数;(2)再求每一列第一个非零元在T.data[]中的位置。对M.data[]一次扫描,可以使所有非零元素在T.data[]中“一次到位”。
(1)数组num[col]
表示矩阵M中第col列非零元素个数;5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
顺序取–直接存(2)数组cpot[col]
指示矩阵M中第col列非零元素在三元组表T.data中恰当位置。由此可以得到cpot初始时的递推关系:5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
顺序取–直接存:按位就座辅助数组num和cpolnum[0]num[1]num[3]num[5]num[2]cpot[0]cpot[1]cpot[3]cpot[4],cpot[5]cpot[2]例5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
顺序取–直接存:按位就座求出cpot[col]后只需要扫描一遍M.data:2684573(2)将cpot[col]+1(下一个col列元素在T.data中的位置)。(1)扫到col列元素时直接将其存放在T.data中下标为cpot[col]的位置;5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
顺序取–直接存算法5-6voidFastTransSMatrix_TSM(TSMatrixM,TSMatrix&T){//用T返回三元组顺序表M的转置矩阵InitSMatrix_TSM(T); //初始化三元组顺序表TT.mu=M.nu; //设置T的行数T.nu=M.mu; //设置T的列数T.tu=M.tu; //设置T的非零元素个数5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
顺序取–直接存:按位就座算法5-6if(T.tu){ for(i=0;i<M.nu;++i)num[i]=0; //num数组初始化 for(i=1;i<=M.tu;++i)++num[M.data[i].col]; //计算每列非零元个数 cpot[0]=1; for(i=1;i<M.nu;++i)cpot[i]=cpot[i-1]+num[i-1];//初始化cpot数组 for(i=1;i<=M.tu;++i){ //扫描M.data,转置 j=M.data[i].col;k=cpot[j]; T.data[k].row=M.data[i].col; T.data[k].col=M.data[i].row; T.data[k].e=M.data[i].e; ++cpot[j]; }}}//FastTransSMatrix_TSM5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●三元组顺序表及操作实现
转置操作●
顺序取–直接存分析①问题规模:矩阵M列数M.nu(nu)和非零元素个数M.tu(tu);②基本操作:行列互换;③时间分析:执行次数依赖于四个并列的for循环,因此算法的时间复杂度为O(nu+tu)。④空间分析:算法中需两个辅助数组存储空间,空间复杂度为O(nu)。
“顺序取-直接存”算法平均时间性能优于“直接取-顺序存”算法,又称快速转置法。但该方法需要两个辅助数组,空间复杂度为O(nu)。5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储当矩阵非零元个数和位置在操作过程中变化较大时,不宜采用三元组顺序表。例如,在进行“将矩阵B加到矩阵A上”的操作时,由于非零元的插入和删除将会引起A.data中元素的大量移动,采用链式存储结构表示三元组的线性表更为合适。5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
十字链表的定义
将稀疏矩阵的非零元素用一个含5个域的结点表示:
稀疏矩阵中每个非零元既是某个行链表又是某个列链表中一个结点,整个矩阵构成一个十字交叉链表;依靠行列指针链接建立相邻逻辑关系的方式称为十字链表。rowdowncoleright非零元行号非零元列号非零元的值列链接指针行链接指针5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
十字链表的定义稀疏矩阵A
通常使用两个一维数组分别存储行链表头指针和列链表头指针。5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
十字链表的类型定义typedefstructOLNode{ //结点int row,col; //非零元的行下标和列下标
ElemType e; //非零元的值
structOLNode *right,*down; //非零元所在行表和列表的后继链域}OLNode,*Olink;typedefstruct{ //十字链表Olink *rhead,*chead;//行和列链表头指针向量
int mu,nu,tu; //稀疏矩阵行列数和总非零元个数}CrossList;
5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
相加操作
对十字链表A和B求和,并将其结果存放在A中,可以从矩阵第一行开始逐行进行。相加之后的和矩阵中非零元素可能有三种情况。
●
结果为aij+bij;
●结果为aij(bij=0);
●结果为bij(aij=0)。
每一行都从行链表头指针出发分别找到A和B在该行中的第一个非零元素结点后开始比较,按照不同的情况分别处理。设计5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
相加操作
设pa和pb分别指向十字链表A和B中行值相同的两个结点,pa为空时表明A在该行中无非零元素。在A的行链表上设pre指针,指示pa所指结点前驱;在A的列链表上设一个指针cp[j],其初值和列链表头指针相同。
初始时,令pa和pb分别指向A和B第0行第一个非零元素结点。重复下面操作,依次处理本行结点,直到B的本行中没有非零元素结点为止:设计5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
相加操作
①pa==NULL或(pa->col)>(pb->col)在A中插入一个值为bij
的结点;此时需要改变同一行中前一结点right域的指针,以及同一列中前一结点down域的指针。②pa!=NULL且(pa->col)<(pb->col)
将pa指向本行的下一个非零元素结点。③
(pa->col)==(pb->col)且((pa->e)+(pb->e))!=0将aij+bij
的值赋给pa所指结点的e域即可,其他所有域的值均不变。④
(pa->col)==(pb->col)且((pa->e)+(pb->e))==0删除A中pa所指结点;此时需要改变同一行中前一结点right域的指针,以及同一列中前一结点down域的指针。设计5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
相加操作算法5-7voidAddSMatrix_OL(CrossList&A,CrossListB){//用A返回十字链表A与B之和
for(j=0;j<M.nu;j++)cp[j]=A.chead[j];for(i=0;i<A.mu;i++){ pa=A.rhead[i]; pb=B.rhead[i]; pre=NULL;5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
相加操作算法5-7 while(pb){ if((pa==NULL)||(pa->col>pb->col)){ p=new(OLNode);
p->row=pb->row; p->col=pb->col; p->e=pb->e; if(!pre)A.rhead[i]=p; //插入到A的行链表中 elsepre->right=p; p->right=pa; pre=p; if(!A.chead[p->col]){
//插入到A的列链表中 A.chead[p->col]=p; p->down=NULL;
}5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
相加操作算法5-7 else{ while((cp[p->col]->down!=NULL) &&(cp[p->col]->down->row<p->row)) cp[p->col]=cp[p->col]->down;
//寻找同一列中的前驱 p->down=cp[p->col]->down;cp[p->col]->down=p; } cp[p->col]=p; } elseif(pa->col<pb->col){
//pa右移一步 pre=pa; pa=pa->right;
}5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
相加操作算法5-7
elseif(pa->e+pb->e){
//直接相加 pa->e+=pb->e; pre=pa; pa=pa->right; pb=pb->right; } else{
//从行、列链表中删除 if(!pre) A.rhead[i]=pa->right; else pre->right=pa->right; p=pa;pa=pa->right; if(A.chead[p->col]==p) A.chead[p->col]=cp[p->col]=p->down; elsecp[p->col]->down=p->down; deletep; } }}}//AddSMatrix_OL5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
相加操作算法5-75.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现
相加操作分析①
问题规模:稀疏矩阵A和B的非零元素的个数A.tu和B.tu;②
基本操作:逐行扫描,元素相加;③时间分析:从一个结点来看,进行比较、修改指针所需要的时间是一个常数;整个运算过程在于对A和B的十字链表逐行扫描,其循环次数取决于A.tu和B.tu。因此算法的时间复杂度为O(A.tu+B.tu)。5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现例5-2
创建十字链表。
(1)初始化行链表的头指针数组和列链表的头指针数组;
(2)将非零元结点插入行链表:如果行链表为空,则直接插入;如果行链表非空,则先寻找插入位置,后插入之;
(3)将非零元结点插入列链表:如果列链表为空,则直接插入;如果列链表非空,则先寻找插入位置,后插入之。设计5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现例5-2
创建十字链表。算法5-8voidCreateSMatrix_OL(CrossList&M){//按任意次序输入非零元,创建稀疏矩阵的十字链表Mcin>>m>>n>>t; //输入稀疏矩阵总行数、列数和非零元个数M.mu=m;M.nu=n;M.tu=t;M.rhead=newOLNode[m+1]; M.rhead[]=NULL; //初始化行头指针向量;各行链表为空链表M.chead=newOLNode[n+1];M.chead[]=NULL; //初始化列头指针向量;各列链表为空链表for(cin>>i>>j>>e;e!=0;cin>>i>>j>>e){p=newOLNode; //生成新结点p->row=i;p->col=j;p->e=e;5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现例5-2
创建十字链表。算法5-8if(M.rhead[i]==NULL){ //完成行插入M.rhead[i]=p;p->right=NULL;}elseif(M.rhead[i]->col>j){ //寻找在行链表中的插入位置p->right=M.rhead[i];M.rhead[i]=p}else{for(q=M.rhead[i];(q->right)&&(q->right->col<j);q=q->right);p->right=q->right;q->right=p;}if(M.chead[j]==NULL){ //完成列插入M.chead[j]=p;p->down=NULL;}elseif(M.chead[j]->row>i){ //寻找在列链表中的插入位置p->down=M.chead[j];M.chead[j]=p}else{for(q=M.chead[j];(q->down)&&(q->down->row<i);q=q->down);p->down=q->down;q->down=p;}}}//CreateSMatrix_OL5.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现例5-2
创建十字链表。算法5-85.2矩阵的压缩存储5.2.2稀疏矩阵的压缩存储●十字链表及操作实现例5-2
创建十字链表。分析①问题规模:待创建稀疏矩阵的总行数、总列数及非零元个数(分别设置为m、n和t);②
基本操作:在十字链表中建立非零元结点;③时间分析:执行时间主要花费在建立非零元结点上。每建立一个非零元结点都需先寻找它在行列链表中的插入位置,再完成插入;对于m行n列,且有t个非零元的稀疏矩阵,算法执行复杂度为O(t
s),s=Max{m,n}。5.3广义表
广义表,又称列表(Lists),是线性表的一种推广和扩充,即取消了对线性表元素的原子限制,允许具有其自身结构;但广义表又区别于数组,即不要求每个元素具有相同类型。
5.3广义表5.3.1广义表的类型定义●广义表的定义
广义表是n(n≥0)个元素a1,a2,…,an
的有限序列,通常记做LS=(a1,a2,…,an)其中:LS是广义表名称;n是LS长度;ai
是LS成员(直接元素),可以是单个元素,也可以是广义表,分别称为广义表LS的原子和子表。(1)当LS非空时,第一个直接元素a1
称为LS的表头,其余的直接元素组成的表(a2,a3,…,an)称为LS的表尾。(2)LS中直接元素的个数称为LS的长度,LS中括号的最大嵌套层数成为LS的深度。
广义表是一种递归定义的数据结构。虽然广义表也是一种线性结构,但和线性表有着明显的差别,正是这一特点使得广义表在处理有层次特点的线性结构问题时有着独特的功能。
5.3广义表5.3.1广义表的类型定义●广义表的定义(1)A=() (4)D=(A,B,C)
(2)B=(e) (5)E=(a,E)(3)C=(a,(b,c,d)) (6)F=(())
例如:下面是6种形式的广义表。
A是一个空表,长度为0,深度为1。B只有一个原子e,长度为1,深度为1。C有两个直接元素,分别为原子a和子表(b,c,d),长度为2,深度为2。
D有三个直接元素,分别为子表A、B和C,长度为3;将上面所述三个子表代入后,则有D=((),(e),(a,(b,c,d))),因此深度为3。E有两个直接元素,分别为原子a和自身,长度为2;E是一个递归表,展开后是一个无限广义表E=(a,(a,(a,…))),因此深度为无穷大。F有一个直接元素,是一个空表,长度为1,深度为2。
广义表是n(n≥0)个元素a1,a2,…,an
的有限序列,通常记做LS=(a1,a2,…,an)其中:LS是广义表名称;n是LS长度;ai
是LS成员(直接元素),可以是单个元素,也可以是广义表,分别称为广义表LS的原子和子表。广义表E(1)A=() (4)D=(A,B,C)
(2)B=(e) (5)E=(a,E)(3)C=(a,(b,c,d)) (6)F=(())广义表A、B、C、D广义表F5.3广义表5.3.1广义表的类型定义矩形结点表示原子,圆形结点表示广义表●广义表的图形表示5.3广义表5.3.1广义表的类型定义●广义表的主要特性
●
广义线性性。
●
元素复合性。
●
元素递归性。
●
元素共享性。广义表中的直接元素彼此之间有着固定的相对次序,如同线性表。广义表中的元素类型可以不统一,可以是原子,也可以是子表。广义表可以是递归的,因此具有较强的表达能力。可以减少存储结构中的数据冗余,以节约存储空间。A=() B=(e) 是D=(A,B,C)的共享子表C=(a,(b,c,d))
广义表结构灵活,可以兼容线性表、数组、树和有向图等各种常用数据结构。例如:
当二维数组每行(列)作为子表处理时,二维数组即为一个广义表;
如果限制广义表中元素的共享和递归,广义表和树对应;
如果限制广义表的递归并允许元素共享,则广义表和图对应。
5.3广义表5.3.1广义表的类型定义●广义表的基本运算因为是空表,所以不能进行取表头和取表尾的操作。
GetHead(B)=e,GetTail(B)=()GetHead(C)=a,GetTail(C)=((b,c,d))
广义表有两个最重要的基本运算:取广义表表头GetHead和取广义表表尾GetTail。
任何一个非空广义表表头是表中第一个元素,它可以是原子,也可以是广义表;而其表尾必定是广义表。例如:(1)A=()(2)B=(e)(3)C=(a,(b,c,d))5.3广义表5.3.1广义表的类型定义●广义表的抽象数据类型ADTGList{Data:
D={ei|ei
AtomSet或ei
GList,i=1,2,…,n,n≥0,AtomSet为某个数据对象}
Relation:
R={<ei-1,ei>|ei-1,ei
D,i=2,…,n}
Operation:
…… GetHead(GL);
初始条件:广义表GL已经存在。 操作结果:取GL的表头。
GetTail(GL);
初始条件:广义表GL已经存在。 操作结果:取GL的表尾。
……}ADTGList广义表的链式表示有两种形式:头尾链表和扩展线性链表。5.3广义表5.3.2广义表的链式表示及操作实现●头尾链表
●
表结点:用来表示广义表。tag域存放区分表结点和原子结点的标志(tag=1),hp域存放指向表头结点的指针,tp域存放指向表尾结点的指针。
●
原子结点:用来表示原子。tag域存放区分表结点和原子结点的标志(tag=0),data域存放原子的数据。
表结点结构原子结点结构1hptp标志域头指针域尾指针域0data数据域标志域
由上述表结点和原子结点组成的广义表链式存储结构称为头尾链表。5.3广义表5.3.2广义表的链式表示及操作实现●头尾链表
例如:
用头尾链表表示广义表A、B、C、D、E、F。
(1)A=()(2)B=(e)、(3)C=(a,(b,c,d))(4)D=(A,B,C)(5)E=(a,E)(6)F=(())1∧0e1∧10a11∧11∧10a0b0c0d1∧1∧A=NILBDEC11∧∧F5.3广义表5.3.2广义表的链式表示及操作实现●头尾链表typedefenum{ATOM,LIST}ElemTag; //ATOM==0:原子,LIST==1:子表typedefstructGLNode{ElemTag tag; //标志域,用于区分原子结点和表结点
union{ //原子结点和表结点的共用体
ElemType data; //原子结点的数据域
struct{ structGLNode*hp,*tp; //hp指向表头,tp指向表尾
}ptr;};}GLNode;typedefGLNode*GList;表结点结构1hptp原子结点结构0data
类型定义5.3广义表5.3.2广义表的链式表示及操作实现●扩展线性链表
●
表结点:用来表示广义表。tag域存放区分表结点和原子结点的标志(tag=1),hp域存放指向表头结点的指针,tp域存放指向下一个结点的指针。
●
原子结点:用来表示原子。tag域存放区分表结点和原子结点的标志(tag=0),data域存放原子的数据,tp域存放指向下一个结点的指针。
表结点结构1hptp标志域头指针域下一个结点指针域
由上述表结点和原子结点组成的广义表链式存储结构称为扩展线性链表。原子结点结构0datatp标志域值域下一个结点指针域5.3广义表5.3.2广义表的链式表示及操作实现●扩展线性链表
例如:
用扩展线性链表表示广义表A、B、C、D、E、F。
(1)A=()(2)B=(e)、(3)C=(a,(b,c,d))(4)D=(A,B,C)(5)E=(a,E)(6)F=(())5.3广义表5.3.2广义表的链式表示及操作实现●扩展线性链表typedefenum{ATOM,LIST}ElemTag; //ATOM==0:原子,LIST==1:子表typedefstructGLNode{ElemTag tag; //标志域,用于区分原子结点和表结点union{ //原子结点和表结点的联合部分AtomType data; //原子结点的值域structGLNode*hp; //表结点的表头指针};structGLNode *tp; //指向下一个结点,相当于链表的next}GLNode;typedefGLNode*GList;5.3广义表5.3.2广义表的链式表示及操作实现●头尾链表的操作实现算法5-9voidInitGList(GList&GL){//构造一个空的广义表头尾链表GLGL=NULL;}//InitGList
初始化操作5.3广义表5.3.2广义表的链式表示及操作实现●头尾链表的操作实现
复制操作设计
假设GL是原始的广义表头尾链表,GT是复制的广义表头尾链表,采用递归方式求解。递归模型如下:
●
基本项:当GL为空表时,置空表GT;
●归纳项:当GL为非空表时,分别复制其表头和表尾。5.3广义表5.3.2广义表的链式表示及操作实现●头尾链表的操作实现
复制操作voidCopyGList(GList>,GListGL){//采用递归方法,实现复制广义表头尾链表GL到GT的操作if(!GL)GT=NULL; //复制空表else{GT=newGLNode; //创建一个表结点if(!GT)Error("Overflow!");GT->tag=GL->tag;if(GL->tag==ATOM)GT->data=GL->data; //复制原子else{ //复制表头和表尾CopyGList(GT->ptr.hp,GL->ptr.hp);CopyGList(GT->ptr.tp,GL->ptr.tp);}}}//CopyGList算法5-105.3广义表5.3.2广义表的链式表示及操作实现●头尾链表的操作实现
求广义表长度操作设计
采用递归方式先求解广义表头尾链表GL表尾的长度,然后再令其加1即为广义表的长度。递归模型如下:
●
基本项:当GL为空表时,表长为0;
●归纳项:当GL为非空表时,表长为表尾长度加1。5.3广义表5.3.2广义表的链式表示及操作实现●头尾链表的操作实现
求广义表长度操作intGListLength(GListGL){//采用递归方式求解并返回头尾链表GL的长度if(GL==NULL)return0;else{
taillen=GListLength(GL->ptr.tp); //递归求解GL表尾的长度return(taillen+1);}}//GListLength算法5-115.3广义表5.3.2广义表的链式表示及操作实现●头尾链表的操作实现
求广义表深度操作设计
采用递归方式从表头开始依次求解广义表GL中所有直接元素的深度,然后令其中的最大深度加1即为广义表的深度。递归模型如下:
●
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 六年级劳动课教学设计:手脑并用种土豆劳动实践悟光荣
- 高中美术必修鉴赏西方古典美术传统与成就教学设计
- 初中化学九年级教学设计:中考一轮复习专题一 化学实验与科学探究
- 初中七年级数学《统计调查》教学设计:基于核心素养的数据感知与推断实践
- 初中物理九年级《电流的磁场》教学设计(沪科版五四制)
- 小学三年级美术《手艺人的故事》教学设计
- 高二物理必修第三册《实验:电池电动势和内阻的测量》教学设计
- 九年级英语Unit 5社交礼仪教学设计
- 译林版英语九年级上册Unit 5 Music speaks Reading教学设计
- 七年级道德与法治下册“严于律己”体验式教学设计与实施
- 2026强制性条文汇编手册
- uhpc模壳柱施工方案
- 打桩工三级安全教育试题及答案
- 上交所技术测评笔试题
- 易制爆化学品各岗位安全责任制
- DB54∕T 0408-2024 羊肚菌菌种生产技术规程
- 雨课堂在线学堂《职业探索与选择》作业单元考核答案
- 应急知识图谱构建-洞察及研究
- 2025届上海市高考英语考纲词汇表
- 电梯特种作业证考试试题及答案
- 精英制模有限公司制模手册
评论
0/150
提交评论