1、模块五 数组、串和广义表,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,书名:数据结构与C+算法设计案例教程 作者:赖俊峰 ISBN:978-7-111-31755-5 出版社:机械工业出版社,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,教材详情:点击查阅 机械工业出版社教材服务网,任务一 数组 任务二 串 任务三 广义表,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,任务一 数组,子任务1 数组的定义 子任务2 数组的基本操作 子任务3 特殊
2、矩阵的压缩存储,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,子任务1 数组的定义,在早期的高级语言中,数组是唯一可供使用的数据类型。由于数组中各元素具有统一的类型,并且数组元素的下标一般具有固定的上界和下界,因此,数组的处理比其它复杂的结构更为简单。数组作为一种数据结构,其特点是结构中的元素本身可以是具有某种结构的数据,但属于同一数据类型,一维数组可以看作一个线性表,二维数组可以看作“数据元素是一维数组”的一维数组,三维数组可以看作“数据元素是二维数组”的一维数组,以此类推。如图5-1表示一个m行n列的二维数组。,数据结构与C+算法设计案例教
3、程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,图5-1中的A数组可看成是由m个行向量或者n个列向量组成的线性表。对于上述二维数组A,这里可以将A看成是下述线性表 A =(d1,d2,dn) 其中每一个数据元素dj本身也是一个线性表 dj=(d1j,d2j,dmj) 1jn。该线性表是二维数组A的第j个列向量。同样,也可将二维数组A看成线性表A =(s1,s2,sm),其中每个si本身也是一个线性表si=(si1,si2,sin) 1im,si是二维数组A的第i个行向量。类似地,一个三维数组可以看成是数据元素为二维数组的线性表。一般地,一个n维数组可视为其数据元素为n-1维
4、数组的线性表。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,二维数组中每个元素ai,j均属于两个向量,第i行的行向量和第j列的列向量。因此,除边界外,每个元素ai,j都恰好有两个直接前趋和直接后继:行向量上的直接前趋ai,j-1和直接后继ai,j+1,列向量上的直接前趋ai-1,j和直接后继ai+1,j;二维数组中有且仅有一个开始结点a11,它没有直接前趋;有且仅有一个终端结点amn,它没有直接后继;边界上的结点(开始结点和终端结点除外)只有一个直接前趋或者只有一个直接后继。即除开始结点a11外,第一行和第一列上的结点a1j(j=2,.,n)
5、,ai1(i=2,.,m)都只有一个直接前趋;除终端结点amn外,第m行和第n列上的结点amj(j=1,.,n-1),ain(i=1,.,m-1)都只有一个直接后继。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,数组一旦被定义,它的维数和维界就不再改变。因此,除了结构的初始化和销毁之外,数组只有存取元素和修改元素值的操作。 数组通常只有两种基本运算读和写。读:给定一组下标,读取相应的数据元素。写:给定一组下标,修改相应的数据元素。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,数组上的基本操作
6、有:,(1) 数组的输入:ArInput ()初始条件:数组存在。操作结果:向数组元素中输入元素。 (2) 求数组的长度:ArLength ()初始条件:数组存在。操作结果:显示数组中有效元素的个数。 (3) 取数组元素:ArGetElem(int n)初始条件:数组存在。操作结果:显示数组指定元素,如果没有则显示空。 (4) 按值查找:ArLocateElem(int m),m是给定的一个数据元素初始条件:数组存在。操作结果:显示m所在的位置,如果没有则显示空。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,(5) 地址操作:ArAddres
7、s(int i)初始条件:数组存在。操作结果:显示第i号元素的地址 (6) 插入操作:ArInsertElem(int i,int j,int k,char*e)初始条件:数组存在 。操作结果:在第i个的第j个的第k个位置插入元素e。 (7) 删除操作:void ArDeleteElem(int i,int j,int k)初始条件:数组存在。操作结果:在第i个的第j个的第k个位置插入元素。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,二维数组的两种存储方式,一种是以列序为主序的存储方式 一种是以行序为主序的存储方式,数据结构与C+算法设计案
8、例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,(a)列序为主序:a11 a21 am1 a12 a22 Am2a1n a2n amn (b)行序为主序:a11 a12 a1n a21 a22 A2nam1 am2 amn 以上规则可以推广到多维数组的情况:优先顺序可规定为先排最右的下标,从右到左,最后排最左下标:列优先顺序与此相反,先排最左下标,从左向右,最后排最右下标。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,按上述两种方式顺序存储的数组,只要知道开始结点的存放地址(即基地址),维数和每维的上、下界,以及每个数
9、组元素所占用的单元数,就可以将数组元素的存放地址表示为其下标的线性函数。因此,数组中的任一元素可以在相同的时间内存取,即顺序存储的数组是一个随机存取结构。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,举例,二维数组Amn按“行优先顺序”存储在内存中,假设每个元素占用d个存储单元。元素aij的存储地址应是数组的基地址加上排在aij前面的元素所占用的单元数。因为aij位于第i行、第j列,前面i-1行一共有(i-1) n个元素,第i行上aij前面又有j-1个元素,故它前面一共有(i-1) n+j-1个元素,因此,aij的地址计算函数为: LOC(a
10、ij)=LOC(a11)+(i-1)*n+j-1*d,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,三维数组Aijk按“行优先顺序”存储,其地址计算函数为: LOC(aijk)=LOC(a111)+(i-1)*n*p+(j-1)*p+(k-1)*d上述讨论均是假设数组各维的下上界是1的情况,更一般的二维数组是Ac1d1,c2d2,这里c1,c2不一定是1。aij前一共有i-c1行,二维数组一共有d2-c2+1列,故这i-c1行共有(i-c1)*(d2-c2+1)个元素,第i行上aij前一共有j-c2个元素,因此,aij的地址计算函数为: LOC
11、(aij)=LOC(ac1c2)+(i-c1)*(d2-c2+1)+j-c2)*d,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,在C+语言中,数组各维下标的下界是0,因此在C语言中,二维数组的地址计算公式为:LOC(aij)=LOC(a00)+(i*(d2+1)+j)*d,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,子任务2 数组的基本操作,数组的主要基本操作包括: 1、向数组元素中输入元素 2、显示数组中有效元素的个数 3、插入元素 4、删除元素,数据结构与C+算法设计案例教程 赖俊峰 9
12、78-7-111-31755-5 高职高专ppt课件,【案例】对学生宿舍楼(包括层数,每层宿舍数,每宿舍学生数)管理创建一个三维数组,#include using namespace std; class DormArray /宿舍楼数组类 private:int cnt; /当前有效元素的个数char* Dormitory6306;/学生宿舍数组,每座宿舍楼有6层,每层有30个宿舍,每个宿舍住6个学生 public:void ArInput(); /向数组元素中输入元素,void ArLength(); /显示数组中有效元素的个数void ArInsertElem(int i,int j,i
13、nt k,char*e); /在第i号楼、第j号宿舍、k号床位插入元素evoid ArDeleteElem(int i,int j,int k); /在第i号楼、第j号宿舍、k号床位删除元素e ;,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,1、向数组元素中输入元素,void DormArray:ArInput() int i,j,k;for(i=0;ii;if(i=-1) break;cinj; cink;coutname;Dormitoryi-1j-1k-1=name;coutnameendl;cnt+; /计数器累加coutendl;,
14、数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,本程序完成初始化学生宿舍数组Dormitory的功能。 首先,清空Dormitory数组(将指针数组都置为空); 然后,逐个向学生宿舍数组中输入元素,输入内容包括学生所在楼层号、宿舍号、床位号与学生姓名,程序会根据楼层号、宿舍号、床位号确定学生所在Dormitoory数组的位置,元素内容为学生姓名。 在输入每个学生信息时,当前有效元素个数cnt,自动累加。最后,在提示“请你依次输入所在楼层号、宿舍号、床位号:”后输入1完成学生录入。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-317
15、55-5 高职高专ppt课件,2、显示数组中有效元素的个数,void DormArray:ArLength() cout“宿舍楼中一共有“cnt“个学生“endl; ,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,本函数的功能是显示学生宿舍数组Dormitory中有效元素(非空元素的个数)。 实现方法:只需输出记录当前Dormitory数组有效元素的个数的变量cnt即可。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,3、插入元素,void DormArray:ArInsertElem(int
16、i,int j,int k,char *e) /在第i号楼、第j号宿舍、k号床位插入元素e int i1,j1,k1;if(Dormitoryi-1j-1k-1!=NULL) cout“本位置已经被其他同学占用了!“endl;else cnt+;Dormitoryi-1j-1k-1=e;for(i1=0;i16;i1+)for(j1=0;j130;j1+)for(k1=0;k16;k1+)if(Dormitoryi1j1k1!=NULL)cout“第“i1+1“层、第“; coutj1+1“号宿舍、第“;coutk1+1“号床位是:“;coutDormitoryi1j1k1endl; ,数据结
17、构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,上面的函数完成安排学生住宿的功能,即判断在第i号楼、第j号宿舍、k号床位插入元素e。 首先,判断要插入的位置是否已被占用,如果已被占用,输出“本位置已经被其他同学占用了!”提示信息,结束程序运行; 否则,将元素e插入相应位置;然后,将当前有效元素的个数(cnt)加1;最后,输出所有学生信息(数组中的所有非空的元素)。,数据结构与C+算法设计案例教程 赖俊峰 978-7-111-31755-5 高职高专ppt课件,4、删除元素,void DormArray:ArDeleteElem(int i,int j,
18、int k) /在第i号楼、第j号宿舍、k号床位删除元素e int i1,j1,k1;if(Dormitoryi-1j-1k-1=NULL) cout“本位置为空!“endl;else cnt-;Dormitoryi-1j-1k-1=NULL;for(i1=0;i16;i1+)for(j1=0;j130;j1+)for(k1=0;k16;k1+)if(Dormitoryi1j1k1!=NULL)cout“第“i1+1“层、第“;coutj1+1“号宿舍、第“;coutk1+1“号床位是:“;coutDormitoryi1j1k1endl; ,数据结构与C+算法设计案例教程 赖俊峰 978-7-
19、111-31755-5 高职高专ppt课件,本函数完成学生搬离宿舍,即:在第i号楼、第j号宿舍、k号床位删除元素e的工作。 首先,判断该位置是否存在学生,若不存在,结束程序运行; 否则,将学生宿舍数组Dormitory中相应元素设为NULL;然后,将当前有效元素的个数(cnt)减1;最后,输出所有学生信息(数组中的所有非空的元素)。,子任务3 特殊矩阵的压缩存储,矩阵在计算机中是以二维数组的方式来存储的,对于一些特殊的矩阵,为了节省存储空间,采用压缩存储的方式来保存矩阵。本子任务主要讨论如下矩阵: 1、对称矩阵 2、三角矩阵 3、稀疏矩阵,1对称矩阵,在一个n阶方阵A中,若元素满足下述性质:a
20、ij=aji 0i,jn-1 则称A为对称矩阵。如图5-2(a)便是一个5阶对称矩阵。,对称矩阵中的元素关于主对角线对称,故只要存储矩阵中上三角或下三角中的元素,让每两个对称的元素共享一个存储空间,这样,能节约近一半的存储空间。如果按“行优先顺序”存储主对角线(包括对角线)以下的元素,其存储形式图5-2(b)所示。,在这个下三角矩阵中,第i行恰有i+1个元素,元素总数为:(n+1)/2。因此,可以按从上到下、从左到右将这些元素存放在一个向量M0n(n+1)/2-1中。展开后即为下面的格式:a00 a10 a 11 a20 a21 a23 an-1 0 an-1 1 an-1 2 an-1 n-
21、1,为了便于访问对称矩阵A中的元素,就必须在aij和Mk之间找一个对应关系。,若i=j,则ai j在下三角形中。ai j之前的i行(从第0行到第i-1行)一共有1+2+i=i(i+1)/2个元素,在第i行上, ai j之前恰有j个元素(即ai0,ai1,ai2,aij-1),因此有关系式:k=i*(i+1)/2+j 0=kn(n+1)/2 若ij,则aij是在上三角矩阵中。因为aij=aji,所以只要交换上述对应关系式中的i和j即可得到:k=j*(j+1)/2+i 0= kn(n+1)/2,对于给定矩阵A中的一个元素aij,根据它的下标(i,j),就能由上述公式确定其在数组M中的位置k。而ai
22、j在内存的存储地址可用下列公式求出(假设每个数据元素占用L个存储单元):LOC(aij)=LOC(Mk)=LOC(M0)+k*L注:上述的推导公式与二维的逻辑结构的起始下标以及存储的起始小标有关。,2三角矩阵,以主对角线划分,三角矩阵有上三角和下三角两种。所谓上(下)三角矩阵是指矩阵的下(上)三角(不包括对角线)中元素均为常数C或零的n阶矩阵.如图5-3所示。,因此,三角矩阵可压缩存储到数组M1n*(n+1)/2+1中,其中c若非0,则存放到数组的最后一个下标变量中。在三角矩阵中的重复元素c可共享一个存储空间,其余的元素正好有n(n+1)/2个,因此,三角矩阵可压缩存储到向量M0n(n+1)/
23、2中,其中c存放在M n(n-1)/2中。上三角矩阵中,主对角线之上的第i行(0in)恰有n-i个元素,按行优先顺序存放上三角矩阵中的元素aij时,aij之前的i行一共有i(2n-i+1)/2个元素,在第i行上,aij前恰好有j-i个元素:aii,aii+1,aij-1。,因此,Mk和aij的对应关系是:i(2n-i+1)/2+j-i 当ijn(n+1)/2 当ij下三角矩阵的存储和对称矩阵类似sak 和aij对应关系是:i(i+1)/2+j ijn(n+1)/2 ij,3稀疏矩阵,设m*n矩阵中有s个非零元素,若s远远小于矩阵元素的总数(即smn),这样的矩阵称为稀疏矩阵。确切的说,令 e=
24、s/(m*n),称e为矩阵的稀疏因子。通常认为e0.05时称之为稀疏矩阵。,稀疏矩阵的压缩存储方法是只存储非零元素,但由于非零元素的分布一般是没有规律的,在存储非零元素的同时,还必须同时记下它所在的行和列的位置(i,j)。对每个非零元素aij,可以用一个三元组(i,j,aij)唯一确定,其中aij表示该非零元素的值,i,j为该元素在原矩阵中的行号和列号。然后将所有非零元素的三元组按一定次序排列在一起构成三元组表。图5-4为稀疏矩阵和对应的三元组表表示,对于一个稀疏矩阵,可以按行序为主搜索其非零元素,并以此放入三元组表中,在三元组表的最下一行表示稀疏矩阵的行数和列数和非零元素的个数,只有这样的三
25、元组表才能还原出原来的稀疏矩阵。,任务二 串,子任务1 串的概念 子任务2 串的存储 子任务3 串的模式匹配算法,子任务1 串的概念,下面介绍另一种具有线性结构的数据结构串,它也是一种顺序表,本子任务内容主要有: 1、串的定义。 2、串的相关术语。 3、串的基本操作。,1串的概念,串是零个或多个字符组成的有限序列。一般记作S= a1 a2 a3 an,其中S 是串名,双引号括起来的字符序列是串值;ai(1in)可以是字母、数字或其它字符。,2串的相关术语,(1)串长度:串中所包含的字符个数称为该串的长度。 (2)空串:长度为零的串称为空串,它不包含任何字符。 (3)空白串:仅由一个或多个空格组
26、成的串称为空白串。空串和空白串的不同,例如 和分别表示长度为1的空白串和长度为0的空串。 (4)子串:串中任意个连续字符组成的子序列称为该串的子串。空串是任意串的子串,任意串是其自身的子串。,(5)主串:包含子串的串被称为主串。 (6)子串在主串中的位置:子串在主串中首次出现时的该子串的首字符对应的主串中的序号,定义为子串在主串中的序号(或位置)。 例如,设A和B分别为A= This is a string B= is则B是A的子串,A为主串。B在A中出现了两次,其中首次出现所对应的主串位置是3。因此,称B在A中的序号(或位置)为3。,串的基本操作有:,(1) StrInput(char st
27、r)初始条件:串str存在。操作结果:向任意一个str赋值。 (2) StrDisplay(char str);初始条件:串str存在。操作结果:显示一个str赋值。 (3) StrClear(char str)初始条件:串str存在。操作结果:清空一个str的值。 (4) StrCopy(char str1,char str2); 初始条件:串str1和str2存在。操作结果:把str1的内容拷贝到str2中。 (5) StrCompare(char str1,char str2); 初始条件:串str1和str2存在。操作结果:比较str1中的内容与str2的内容是否相同,显示结果,(6)
28、 StrSubstring(char str,int p,int length);初始条件:串str存在 。操作结果:显示一个str中p位置开始长度为length的子串。 (7) voidStrConcat(char str1,char str2,char str3);初始条件:串str1和str2存在。操作结果:把str2和str3的内容进行连接,存入str1中。 (8) StrIndex(char str1,char str2); 初始条件:串str1和str2存在。操作结果:在str1中寻找与str2一样的字符串,显示所在的位置,没有则显示“无此串“,用简单模式匹配算法实现。 (9) v
29、oid StrKMP(char str1,char str2); 初始条件:串str1和str2存在。操作结果:在str1中寻找与str2一样的字符串,显示所在的位置,没有则显示“无此串“,用KMP算法实现(重要),子任务2 串的存储,串的存储方式主要有三种,本子任务主要讲解: 1、定长顺序存储表示 2、堆分配存储表示 3、串的链式存储结构,1定长顺序存储表示,定长顺序存储表示是用一组连续的存储单元来存放串中的字符序列,可直接使用定长的字符数组来定义,数组的上界预先给出:#define maxstrlen 256typedef char sstringmaxstrlen;sstring s;
30、/s是一个可容纳255个字符的顺序串。,例如,在C+语言中使用一个不会出现在串中的特殊字符 0 在串值的尾部来表示串的结束。所以串空间最大值maxstrlen虽然为256,但最多只能存放255个字符,必须留一个字节来存放 0 字符。若不设终结符,可用一个整数来表示串的长度,那么该长度减1的位置就是串值的最后一个字符的位置。此时顺序串的类型定义和顺序表类似:class sstring public:char chmaxstrlen;int length; /其优点是涉及到串长操作时速度快。,2堆分配存储表示,在这种存储表示下,仍以一组地址连续的存储单元存放串值字符序列,但它们的存储空间是在程序执
31、行过程中动态分配而得。在C+语言中,利用动态存储管理函数,来根据实际需要动态分配和释放字符数组空间。这样定义的顺序串类型如下:class hsringchar *ch;int length;,3串的链式存储结构,在顺序串上的插入和删除操作不方便,需要移动大量的字符。因此,这里可用单链表方式来存储串值,串的这种链式存储结构简称为链串。class lstring char data;lstring *next;,这种结构便于进行插入和删除运算,但存储空间利用率太低。为了提高存储密度,可使每个结点存放多个字符。通常将结点数据域存放的字符个数定义为结点的大小,显然,当结点大小大于1时,串的长度不一定正
32、好是结点的整数倍,因此要用特殊字符来填充最后一个结点,以表示串的终结。其类型定为: #define nodesize 80class lstring char datanodesize;lstring *next;,子任务3 串的模式匹配算法,串的操作包括: 1、串的基本操作 2、串的模式匹配算法 3、改进的模式匹配算法(KMP算法),1、串的基本操作,对于串的基本操作,许多高级语言均提供了相应的运算或标准库函数来实现。在C+语言中,常用的串运算有串的赋值运算、求串的长度运算、串连接运算、串比较运算和求子串运算,上述串的操作是最基本的,故称为串运算的最小子集。串的其余操作可由这些基本操作组合而
33、成。,2模式匹配算法,子串定位运算又称为模式匹配或串匹配,就是在主串中取从第i个字符起、长度和串T相等的子串和T比较,若相等,则求得函数值为i,否则值增1直至S中不存在和串T相等的子串为止。,在串匹配中,一般将主串称为目标串,子串称之为模式串。设S为目标串,T为模式串,且不妨设:S=“s0s1s2sn-1“ T=“t0t1tm-1“ 串的匹配实际上是对于合法的位置0=i=n-m依次将目标串中的子串sii+m-1和模式串t0m-1进行比较,若sii+m-1=t0m-1,则称从位置i开始的匹配成功,亦称模式t在目标s中出现;若sii+m-1 t0m-1,则称从位置i开始的匹配失败。上述的位置i又称
34、为位移,当sii+m-1=t0m-1时,i称为有效位移;当sii+m-1 t0m-1时,i称为无效位移。这样,串匹配问题可简化为是找出某给定模式T在一给定目标T中首次出现的有效位移。,串匹配的算法很多,这里先讨论一种最简单的称为朴素的串匹配算法。其基本思想是用一个循环来依次检查n-m+1个合法的位移i(0in-m)是否为有效位移,例如:for(i=0;i=n-m;i+)if(Sii+m-1=T0m-1)return i;,下面以定长的顺序串类型作为存储结构,给出具体的串匹配算法。,int index(sstring s,sstring t,int pos)int i,j,k;int m=s.l
35、ength;int n=t.length;for(i=0;i=n-m;i+)j=0;k=i;while(jm 显然,该算法在最坏情况下的时间复杂度为O(n-m)m)。,3、改进的模式匹配算法(KMP算法),这个算法是D.E.Knuth与V.R.Pratt和J.H.Morris三个人同时发现的,因此人们称它为克努特-莫里斯普拉特操作,简称为KMP算法。此算法可以在O(n+m)的时间数量及上完成串的模式匹配操作。其改进在于每当在一趟匹配过程中出现对应位置的字符不相等时,其字符指针并不回退,而是利用已经得到的“部分匹配”的结果将模式向右滑动尽可能远的一旦距离后,继续进行比较。,例如有主串S=“a b
36、 a b c a b c a c b a“ 和子串T=“a b c a c“,按简单的匹配算法进行时,其匹配过程如图5-5:,仔细观察在图5-5(b)比较不成功时,因为模式串中第一个字符是a,故不需要和i=4、i=5、和i=6进行比较,而仅需将模式向右滑动三个字符的位置继续比较i=7和j=2的字符是否相等即可。同理在第一次比较失败(图5-5(a)后,只需将模式串的指针j修改为j=1,而主串继续加1(i=3)即可,由此可得在整个匹配过程中,i的指针没有回退,这就是KMP的主要思想。,现在讨论一般情况,假设主串S1Sn, 模式串P1Pm,KMP算法需解决下述问题,当匹配过程产生失败(Si不等于Pj
37、)时,指针j需要向右滑动多大的距离,而使得i不回退继续进行下一次的比较。假设此时应与模式串中第k(kj)个字符,则模式串中的前k-1个字符的子串必须满足下列的关系,且不可能存在大于k的其它值满足此关系。 P1Pk-1 = Si-(k-1) Si-1 (5-1)式 已经得到的部分匹配可知如下的关系式成立: Pj-k+1Pj-1 = Si-(k-1) Si-1 (5-2)式由4-1式和4-2式可得以下结果 P1Pk-1 = Sj-(k-1) Sj-1 (5-3)式,反之,若模式串中存在满足5-3式的两个子串,则当匹配过程中,主串的第i个字符与模式串的第j个字符比较不等时,仅需将模式向右滑动到模式串
38、中第k个字符与主串的第i个字符对齐即可继续比较。现在的问题是求解子串的第j个字符比较失败是如何求解下一个比较位置,用nextj数组来表示第j个字符比较失败时下一个比较位置。由上面的5-3式可以得到图5-6的关系:,一旦失配,应从模式串T中第next j 个字符开始与S的失配点i重新匹配。,推算模式串“a b a a b c a c”的next函数值如下:,j=1时, next j 0;因为属于“j=1”; j=2时, next j 1;因为属于“其他情况”; j=3时, k=2,只需查看T1=T2; j=4时, k=2,3,要查看T1=T3 和T1T2=T2 T3 j=5时, k=2,3,4,
39、要查看T1=T4 、T1T2=T3T4 和T1T2T3=T2T3T4 由上面的求解可得到如下的next的求解值。可能失配位 j: 1 2 3 4 5 6 7 8 模 式 串 T: a b a a b c a c 新匹配位 nextj : 0 1 1 2 2 3 1 2,KMP算法在形式上与简单模式匹配算法极为相似,不同之处仅在于当匹配过程产生失败时,指针i不变,指针j退回到nextj所指示的位置上重新进行比较,并且当指针j退回到0时,指针i和指针j需要同时增1。,下面给出串的最基本几个算法,char str1100,str2100; void AdressString:StrIndex(cha
40、r str1,char str2) /在str1中寻找与str2一样的字符串,显示所在的位置,没有则显示“无此串“, /用简单模式匹配算法实现(重要) int i=0,j=0;while(istrlen(str1) /jstrlen(str2,)匹配不成功 ,用简单模式匹配算法实现在str1中寻找与str2一样的字符串并显示所在的位置,没有则显示“无此串”。首先,定义两个初始值为0的变量i、j,分别标记str1与str2的下标。然后,从两个字符串的开始位置,逐个匹配。若匹配成功,则i和j同时加1,否则,i赋值为开始匹配成功字符的下一个字符,j重新赋值为0。直到循环条件不成立为止。最后,判断变量
41、j的值,若j=strlen(str2)说明匹配成功了str2中的所有字符,所在位置为ij。,void AdressString:StrKMP(char str1,char str2) /在str1中寻找与str2一样的字符串,显示所在的位置,没有则显示“无此串“, /用KMP算法实现(重要) int i=0,j=0,next100;while(istrlen(str1) /jstrlen(str2),匹配不成功 ,用KMP算法实现在str1中寻找与str2一样的字符串并显示所在的位置,没有则显示“无此串“。首先,定义两个初始值为0的变量i、j,分别标记str1与str2的下标。然后,从两个字符
42、串的开始位置,逐个匹配。若匹配成功,则i和j同时加1,否则,i值不变,j使用函数getnext(next)重新赋值。直到循环条件不成立为止。最后,判断变量j的值,若j=strlen(str2)说明匹配成功了str2中的所有字符,调用函数StrIndex(str1,str2)输出相应成功信息。,int AdressString:getnext(int next) int i=0,j=0;next0=0;while(jstrlen(str2)if(j=0 | str2i=str2j)i+;j+;nexti=j;else j=nextj;return j; ,该函数完成的功能是,根据str1自身的性
43、质,求出如果遇到不匹配字符时在指向str1的下标不变的情况下,指向str2的下标需要回溯的距离。,任务三 广义表,广义表也是一种线性表,本任务内容围绕以下几个方面展开: 1、广义表的定义 2、广义表的存储结构 3、广义表的构造及其算法,1广义表的定义,广义表是n(n=0)个元素d1,d2,dn的有限序列,其中di或者是原子项,或者是一个广义表。通常记作LS=(d1,d2,dn)。LS是广义表的名字,n为它的长度。若di是广义表,则称它为LS的子表,为了区别原子和广义表,书写时用大写字母表示广义表,用小写字母表示原子。,广义表具有如下的结构特点:,(1) 广义表中的数据元素有相对次序。 (2)
44、广义表的长度定义为最外层包含的元素个数。 (3) 广义表的深度定义为所含括弧的重数。在广义表中“原子”的深度为“0”;“空表”的深度为1。 (4) 表头可以是原子或列表;表尾必定是列表。 (5) 广义表可以是一个递归的表,递归表的深度是 无穷值,长度是有限值。 (6) 任何一个非空广义表LS =(d1,d2,dn) 均可分解为:表头 Head(LS) = d1和表尾Tail(LS) = ( 2, , n)两部分。,例子,A=( ) A是一个空表,它的长度为零 B=(e) 列表B只有一个原子e,B的长度为1. C=(a,(b,c,d) 列表C的长度为2,两个元素分别为原子a和子表(b,c,d)
45、D=(A,B,C) 列表D的长度为3,三个元素都是列表,显然,将子表的值代入后,则有D=(),(e),(a,(b,c,d) E=(a,E) 这是一个递归的表,它的长度为2,E相当于一个无限的列表E=(a,(a,(a,.),对于确定的表头和表尾可以唯一确定一个广义表,因而,区分表头、表尾十分重要。例如:,Head (A)=() ,Tail (A)=();Head (B)=e ,Tail (B)=(); Head (C)=a ,Tail (C)=(b,c,d);Head (C)=a ,Tail (C)=(b,c,d); Head (D)=A ,Tail (D)=(B,C);Head (E)=a ,
46、Tail (E)=(E) Head( ( b, c) ) = ( b, c)Tail( ( b, c) ) = ( ) Head( a,( b, c) ) = a Tail( a,( b, c) ) = ( b,c ) Head( ( c ) ) =(c)Tail( ( c ) ) = ( ),2广义表的存储结构,广义表的元素类型不是单一的,故难于使用顺序结构来存储它,通常使用链式结构来存储。广义表中的每一个元素可用一个结点来表示。由于广义表元素只有两类:原子和子表,因此,在广义表链式结构中也就只有两类结点:原子结点和表结点,其结点形式如图5-7。,可以把链式结构的广义表类比于树的孩子-兄弟链
47、结构表指针指向最外层的表结点,该结点的hp指向该表的第一个元素,tp指向其表尾。tp链上的结点个数等于该层次的元素个数。可以说,tp链上的所有结点的hp域所指向的元素都是同层次的元素。,用以上的结点结构就可以画出相应的存储结构图,图5-8是广义表C=(a , ( b , c , d ) 的存储表示。,下面给出链式结构下广义表的存储表示及相应的广义表的构造过程。,#include using namespace std; class GLNodeAtom /原子结点类 public:char a; /字符变量 ; class GLNode /表结点类 public:GLNode* hp; /指向表头结点GLNode* tp; /指向表尾结点GLNodeAtom* Atom; /指向原子结点的指针 ;,int main() GLNode GLN1,GLN2,GLN3,GLN4; /定义了4个GLNode的对象GLNodeAtom GLNA1,GLNA2,GLNA3; /定义了3 个GLNodeAtom 的对象cout“构造的广义表为:(a,(b,c)”endl; /构造广义表GLNA1.a=a;GLNA2.a=b;GLNA3.a=c;GLN1.hp=NULL;GLN1.tp= ,