1、数据结构第四章第四章 数组和串数组数组的定义及其基本操作数组的存贮结构 特殊矩阵的压缩存储稀疏矩阵的压缩存储数组的定义及其基本操作数组的定义:数组(Array):是n(n1)个相同类型的数据元素a0 , a1 , ,an-1构成的有限序列,且该有限序列存储在块地址连续的内存单元中。(即物理存储与逻辑结构相一致) 数组中的数据元素数目固定。 数组中的每个数据元素具有相同的数据类型。 数组中的每个数据元素都和一组唯一的下标值相对应。 数组是一种随机存储结构,可随机存取数组中的任意数据元素。 地址计算:一维数组同线性表相同;计算公式:Loc(ai) = Loc(a0) + i*k (0= i n)注
2、:Loc(a0) 是a0元素的地址, Loc(ai)是ai元素的地址,k是每个元素所占的存储单元数。 二维数组设m行n列的二维数组Am*n,有 地址计算:1、按行序列序计算:Loc( aij)=Loc(a11)+(i-1)n+(j-1)*l 注: k是每个元素所占的单元数。2、按列序序列计算:Loc(aij)=Loc(a11)+(j-1)m+(i-1)*l 数组的基本操作 1. arraylength(D) 2. Storage(D, i, x) 3. Get(D, i, x) 数组的存储结构数组一般在计算机中采用线性结构存储,对于一维数组同线性表相同;关于二维数组和多维数组,一般语言中采用行
3、序列序,即先存第一(或零)行,然后第二行依次存储。数组也允许有两种存储分配方法:静态和动态。 静态数组:运行期间数组大小不能改变,需预先给出。 动态数组:可以动态建立和动态撤消的数组。动态数组:一维动态数组 int *a;a=(int *)malloc(n*sizeof(int);a0an-1free(a);二维动态数组 int *a,i;a=(int *)malloc(row *sizeof(int *);for (i=0;irow;i+) ai=(int *)malloc(col*sizeof(int);释放空间free(a)? for(i=0;irow;i+) free(ai);free
4、(a);特殊矩阵的压缩存储*特殊矩阵1、对称矩阵的压缩存储对称矩阵 定义:设n 阶方阵中的元素关于主对角线对称, 即满足条件:aij = aji 1=i , j =j 时 k=j(j-1)/2+i-1 当 ji 时则n(n+1)/2为n阶对称方阵的压缩存储(下三角存储即用行序列序)。2、 n阶下三角矩阵 3、对角矩阵Loc(aij)=Loc(a11)+2(i-1)+(j-1) 4、稀疏矩阵的压缩存储定义:非零元较零元少,且分布没有一定规律的矩阵假设在m*n的矩阵中,有t个元素不为零,令 ,称 为矩阵的稀疏因子。通常认为 0.05时称为稀疏矩阵。稀疏矩阵M和TM由(1,2,12), (1,3,9
5、), (3,1,-3), (3,6,14), (4,3,24), (5,2,18), (6,1,15), (6,4,-7) 和矩阵维数(6,7)唯一确定*压缩储存 稀疏矩阵的压缩存储方法是只存非零元素,但由于稀疏矩阵元素中的非零元素是没有规律的,所以在存储时还必须存储每个元素的行下标和列下标值。这样稀疏矩阵中的每一个非零元素需由一个三元组(i,j,aij)唯一确定。稀疏矩阵的压缩存储方法:链式存储结构三元组十字链表稀疏矩阵的三元组也可采用链式存储结构。单链表结构, 缺点:操作复杂度高由链表的非随机存取特性限定的。十字链表*设行指针数组和列指针数组,分别指向每行、列第一个非零元*结点定义type
6、def struct node int row,col,val; struct node *down, *right;JD;从键盘接收信息建立十字链表算法1、初始化头指针向量2、初始化各行列链表3、动态生成结点,输入非零元4、将结点插入行5、将结点插入列Status CreateSMatrix_OL(CrossList &M) /创建稀疏矩阵M。采用十字链表存储表示if (M) free(M);scanf(&m,&n,&t); /输入M的行数、列数和非零元个数 M.mu=m; M.nu=n; M.tu=t; if (!(M.rhead=(Olink *) malloc (m+1)*sizeof
7、(Olink) exit (overflow); if (!(M.chead=(Olink *) malloc (n+1)*sizeof(Olink) exit (overflow); M.rhead =M.chead =NULL; /初始化行列头指针向量; /各行列链表为空链表 for (scanf(&i,&j,&e); i!=0;scanf(&i,&j,&e) /按任意次序输入非零元 if (!(p=(OLNode *) malloc (sizeof(OLNode) exit (overflow); p-I=I; p-j=j; p-e=e; /生成结点If (M.rheadI=NULL |
8、 M.rheadI-jj) p-right=M.rheadI;M.rheadI=p;Else /寻查在行表中的插入位置 for (q=M.rheadI; (q-right)&q-right-jright) p-right=q-right; q-right=p; /完成行插入If (M.cheadj=NULL | M.cheadj-ii) p-down=M.cheadj;M.cheadj=p; Else /寻查在列表中的插入位置 for (q=M.cheadj; (q-down)&q-down-idown) p-down=q-down; q-down=p; /完成列插入 /for /ifRetu
9、rn OK;/CreateSMatrix_OL稀疏矩阵的压缩存储方法:顺序存储结构三元组顺序表三元顺序表的定义#define MAXSIZE 20typedef struct int i,j; Elemtype e;Triple;typedef struct Triple dataMAXSIZE+1; int mu,nu,tu;TSMatrix;TSMatrix M;矩阵运算 矩阵运算通常包括:矩阵转置、矩阵加、矩阵减、矩阵乘、矩阵求逆等。求矩阵的转置运算(最简单的运算) 对于一个m*n的矩阵Am*n,其转置矩阵是一个n*m的矩阵,为Bn*m,满足aij = bji , 要实现三元组顺序表的转
10、置,只要实现以下两点:第一点:把矩阵中元素的下标转置后,送入矩阵中;第二点:对矩阵中的元素按行序列序进行排序。方法一:按M的列序转置即按T.data中三元组次序依次在M.data中找到相应的三元组进行转置。为找到M中每一列所有非零元素,需对其三元组表M.data从第一行起扫描一遍。由于M.data中以M行序为主序,所以由此得到的恰是T.data中应有的顺序算法: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;
11、+col) for (p=1; p=M.tu; +p) if (M.datap.j=col) T.dataq.i=M.datap.j; T.dataq.j=M.datap.i; T.dataq.e=M.datap.e; +q; return OK;方法二:稀疏矩阵的快速转置 算法思想是:以M矩阵的三元组为中心, 依次取出 M.data 中的每一个三元组,交换行列后,直接将其写入T.data合适的位置中。实现:引入两辅助数组numcol:存储矩阵M中第col列中非零元个数cpotcol:存储M中第col列第一个非零元在T.data中位置显然有: 算法描述:Status FastTranspose
12、SMatrix(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) numcol=0; for (t=1; t=M.tu; +t) +numM.datat.j;cpot1=1; for (col=2; col=M.nu; +col) cpotcol=cpotcol-1+numcol-1;for (p=1; p=M.tu; +p) col=M.datap.j; q=cpotcol; T.dataq.i=M.datap.j; T.dataq.j=M.datap
13、.i; T.dataq.e=M.datap.e; +cpotcol; return OK;求矩阵乘运算行逻辑链接的顺序表:有时为了方便某些矩阵运算,我们在按行优先存储的三元组中,加入一个行表来记录稀疏矩阵中每行的非零元素在三元组表中的起始位置。当将行表作为三元组表的一个新增属性加以描述时,我们就得到了稀疏矩阵的另一种顺序存储结构:“带行链接信息”的三元组表。 其类型描述如下: #define maxsize 100 typedef struct triple datamaxsize+1;/非零元三元组表 int rposmaxrc+1; /各行第一个非零元的位置表 int n,m,t; /矩阵
14、的行数、列数和非零元个数 TSMatrix;下面讨论两个稀疏矩阵相乘的例子,容易看出这种表示方法的优越性。若设Q=M*N其中,M是m1*n1矩阵,N是m2*n2矩阵。常规的二维数组表示时转置的算法: 当n1=m2时, 有: for(i=1;i=m1;+i) for(j=1;j=n2;+j) qij=0; for(k=1;k=n1;+k) qij+=mik*nkj; 此算法的复杂度为O(m1*n1*n2)。行逻辑链接的顺序表从M和N求得Q1、求乘积矩阵Q中元素 由矩阵乘法规则知: Q(i,j)=M(i,1)N(1,j)+M(i,2)N(2,j)+M(i,n)N(n,j) 这就是说,为求Q的值,只
15、有M(i,k)与N(k,j)(即M元素的列与N元素的行相等的两项)才有相乘的机会,且当两项都不为零时,乘积中的这一项才不为零。 即只找到M.data中的j值和N.data中的i值相等的各对元素相乘即可。2、相乘的基本操作是:对于M中每个元素M.datap,找到N中所有满足条件M.datap.j=N.dataq.i的 元素N.dataq,求得M.datap.e和M.dataq.e的乘积。即M11只有可能和N中第1行的非零元素相乘,M12只有可能和N中第2行的非零元素相乘,而同一行的非零元是相邻存放的,所以求Q11和Q12同时进行,当然只有Mik和Nkj(列号与行号相等)且均不为零(三元组存在)时
16、才相乘,并且累加到Qij当中去。为了便于N.data中寻找N中的第row行第一个非零元素,与前面类似,在此需引入num和rpos两个向量。numrow表示矩阵N中第row行的非零元素的个数;rposrow表示第row行的第一个非零元素在N.data中的位置。于是有 rpos1=1 rposrow=rposrow-1+numrow-1 2rown 3、根据以上分析,稀疏矩阵的乘法运算的粗略步骤如下:初始化。清理一些单元,准备按行顺序存放乘积矩阵;求N的num,rpos;做矩阵乘法。将M.data中三元组的列值与N.data中三元组的行值相等的非零元素相乘,并将具有相同下标的乘积元素相加。 例如:
17、算法描述:Status MultSMatrix(TSMatrix M, TSMatrix N, TSMatrix &Q) /求矩阵乘积Q=M* N,采用行逻辑链接存储。设M.nu=N.muif (M.nu != N.mu) return ERROR;if (M.tu*N.tu != 0) / Q是非零矩阵Q.mu = M.mu; Q.nu = N.nu; Q.tu = 0; p=1 / Q初始化do crow=M.datap.i; /crow指示当前处理的行号 ctemp = 0; / 当前行各元素累加器清零 while (p=M.tu & M.datap.i=crow) brow=M.dat
18、ap.j; /找到对应元在N中的行号 for (q=N.rposbrow; qrposbrow+1; +q) ccol = N.dataq.j; / 乘积元素在Q中列号 ctempccol += M.datap.e * N.dataq.e; / for q +p; /求得Q中第crow行的非零元for (ccol=1; ccol=Q.nu; +ccol) / 压缩存储该行非零元 if (ctempccol) +Q.tu; Q.dataQ.tu = (crow, ccol, ctempccol); / if / do while (pjpb-j,则需要在A矩阵的链表中插入一个值为bij的结点。此
19、时,需改变同一行中前一结点的right域值,以及同一列中前一结点的down域值。2、若pa-jj,则只要将pa指针往右推进一步。3、若pa-j=pb-j且pa-e+pb-e!=0,则只要将aij+bij的值送到pa所指结点的e域即可,其他所有域的值都不变。4、若pa-j=pb-j且pa-e+pb-e=0,则需要在A矩阵的链表中删除pa所指的结点。此时,需改变同一行中前一结点的right域值,以及同一列中前一结点的down域值。广义表 广义表的概念: 广义表也称为列表,是线性表的一种扩展,也是数据元素的有限序列。 记作:LS= (d0, d1, d2, . . . . . .dn-1)。其中di
20、既可以是单个元素,也可以是广义表。说明 1)广义表的定义是一个递归定义,因为在描述广义表时又用到了广义表; 2)在线性表中数据元素是单个元素,而在广义表中, 元素可以是单个元素, 称为单元素(原子),也可以是广义表,称为广义表的子表;3)n 是广义表长度;4)下面是一些广义表的例子A = ( ) 空表,表长为0;B = (a,(b,c,d) B的表长为2,两个元素分别为 a 和子表(b,c,d);C = (e) C中只有一个元素e,表长为1;D = (A,B,C,f ) D 的表长为4,它的前三个元素 A,B,C 广义表,第四个 f 是单元素;E=( a ,E ) 递归表.5)若广义表不空,则
21、可分成表头和表尾,反之,一对表头和表尾可唯一确定广义表对非空广义表:称第一个元素为L的表头,其余元素组成的表称为LS的表尾;B = (a,(b,c,d) 表头:a 表尾 (b,c,d) 即 HEAD(B)=a, TAIL(B)=(b,c,d),由表头、表尾定义可知:任何一个非空列表其表头可能是原子,也可能是列表,而其表尾必定为列表。C = (e) 表头:e 表尾 ( )D = (A,B,C,f ) 表头:A 表尾 (B,C,f )运算可以嵌套,如:HEAD(TAIL(B)=b, TAIL(TAIL(B)=(c, d) 。广义表的元素之间除了存在次序关系外,还存在层次关系。如:广义表的存储结构
22、由于广义表中数据元素可以具有不同结构,故难以用顺序结构表示广义表。通常采用链表存储方式 如何设定链表结点?广义表中的数据元素可能为单元素(原子)或子表,由此需要两种结点:一种是表结点,用以表示广义表;一种是单元素结点,用以表示单元素(原子)。按结点形式的不同,广义表的链式存储结构又可以分为不同的两种存储方式。一种称为头尾表示法,另一种称为孩子兄弟表示法。头尾表示法 孩子兄弟表示法广义表链表结点的类型定义如下: Typedef struct GLNode int tag; /标志域:用于区分原子结点和表结点 union int atom; /原子结点的值域 struct GLNode *hp;
23、/表结点的表头指针域, ; struct GLNode *tp; /指向下一个元素的指针 *GList;广义表的基本操作的递归算法广义表是递归结构,所以广义表的许多操作可以用递归算法实现求广义表的深度 int depth(NODE *head)广义表 L= (1, 2。n) 的深度=广义表中括号重数串本章主要内容:1、串的基本概念2、串的存储结构3、串的基本操作4、串的模式匹配算法5、串的应用-文本编辑本章重点:串的存储与基本操作。本章难点:串基本操作的灵活使用。串的基本概念串的定义: 串(或字符串)(String)是由零个或多个字符组成的有限序列。一般记作 s=s0s1s2sn-1 (n0)
24、 其中:s为串名,用双引号括起来的字符序列是串的值;si(0in-1)可以是字母、数字或其它字符;双引号为串值的定界符,不是串的一部分;字符串字符的数目n称为串的长度。零个字符的串称为空串,通常以两个相邻的双引号来表示空串,如:s=,它的长度为零;仅由空格组成的的串称为空格串,如: s=;若串中含有空格,在计算串长时,空格应计入串的长度中,如: s=Im a student的长度为13。请读者注意,在C语言中,用单引号引起来的单个字符与单个字符的串是不同的, 如s1=a与s2=a两者是不同的,s1表示字符,而s2表示字符串。串的基本操作 对于串的基本操作,许多高级语言均提供了相应的运算或标准库
25、函数来实现。下面仅介绍几种在C语言中常用的串运算,其它的串操作见的文件。定义下列几个变量:char s120=“dirtreeformat”,s220=“file.mem”; char s330,*p;int result;(1)串复制 strcpy(str1,str2) 赋值:把str2指向的字符串拷贝到str1中,返回str1。库函数和形参说明如下:char * strcpy(char * str1,char * str2) 例如:strcpy(s3,s1); /s3=“dirtreeformat”(2) 求串长 strlen(str) 求字符串的长度:统计字符串str中字符的个数(不包括
26、0),返回字符的个数,若str为空串,则返回值为0。库函数和形参说明如下:unsigned int strlen(char *str) 例如:printf(“%d”,strlen(s1); 输出13(3)串比较 c函数:int strcmp(char s1,char s2); 该函数比较串s1和串s2的大小,当返回值小于0,等于0或大于0时分别表示 s1s2 例如:result=strcmp(“baker”,”Baker”) result0 result=strcmp(“12”,”12”); result=0 result=strcmp(“Joe”,”Joseph”); result0(4)串
27、联接 char strcat(char *to,char *from) 该函数将串from复制到串to的末尾,并且返回一个指向串to的开始处的指针。 例如:strcat(s3,”/”) strcat(s3,s2); /s3=“dirtreeformat/file.mem”(5)字符定位 char strchr(char *s,char c); 该函数是在串s中寻找字符c第一次出现的位置,若找到则返回该位置,否则返回NULL。 例如:p=strchr(s2,”.”); p 指向“file”之后的位置(6)求子串 substr(s,t,i, k) 求子串的过程即为复制字符序列的过程,将串S中的第i个字符开始长度为k的字符串。0=ilength(s) 0=k=length(s)-i 则t 中值为从串s 的第start个字符, 起长度为len 的字符序列, 并且函数返回值为1, 否则为0;(7)置换 Replace(s,t,v) 若主串s中存在与串t相等的子串则用v替换主串。(8)插入 insert(s,i,t)若0=I=length(s),则在串s的第i个字符之前插入串t。自定
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1