串
串(String):零个或多个任意字符组成的有限序列
- 空串用∅表示
子串:一个串中任意个连续字符组成的子序列(含空串)称为该串的子串
真子串是指不包含自身的所有子串
几个术语
- 子串:串中任意个连续字符组成的子序列称为该串的子串
- 主串:包含子串的串相应的称为主串
- 字符位置:字符在序列中的序号为该字符在串中的位置
- 子串位置:子串第一个字符在主串中的位置
- 空格串:由一个或多个空格组成的串,与空串不同
串相等:当且仅当两个串的长度相等并且各个对应位置上的字符都相同时,这两个串才是相等的。
串的顺序存储结构
#define MAXLEN 255
typedef struct
{
char ch[MAXLEN+1];//存储串的一维数组
int length;//串的当前长度
}SString;
串的链式存储结构
- 优点:操作方便
- 缺点:存储密度较低
存储密度=串所占的存储/实际分配的存储 - 可将多个字符放在一个结点中,以克服其缺点
#define CHUNKSIZE 80 //块的大小可由用户定义
typedef struct Chunk
{
char ch[CHUNKSIZE];
struct Chunk *next;
}Chunk;
typedef struct
{
Chunk *head,*tail;//串的头指针和尾指针
int curlen;//串的当前长度
}LString; //字符串的块链结构
串的模式匹配算法
算法目的:
- 确定主串中所含子串(模式串)第一次出现的位置(定位)
算法应用:
- 搜索引擎、拼音检查、语言翻译、数据压缩
算法种类:
- BF算法(Brute-Force,又称古典的、经典的、朴素的、穷举的 )
- KMP算法(特点:速度快)
BF算法,亦称简单匹配算法,采用穷举的思路
- 算法的思路是从主串的每一个字符开始依次与子串的字符进行匹配 匹配失败要回溯 i=i-j+2 j=1(从头开始)
Index(S,T,pos)
- 将主串的第pos个字符和模式串的第一个字符比较
- 若相等,继续逐个比较后续字符
- 若不等,从主串的下一个字符起,重新与模式串的第一个字符比较
- 直到主串的一个连续字符序列和模式串相等。返回值为S(主串)中与T(模式串)匹配的子序列的第一个字符的序号,即匹配成功。否则匹配失败,返回0
int Index_BF(SString S,SString T,int pos)
{
int i=pos;
int j=1;
while(i<S.length&&j<=T.length)
{
if(S.ch[i]==T.ch[i])//主串和子串依次匹配下一个字符
{
++i;
++j;
}
else//主串,子串指针回溯重新开始下一次匹配
{
i=i-j+2;
j=1;
}
if(j>=T.length)
return i-T.length;//返回匹配的第一个字符的下标
else
return 0;//模式匹配不成功
}
}
- 若主串长度n,子串长度m,算法复杂度O(m*n)
KMP算法
- 定义next[j]函数,表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需重新与主串中该字符进行比较的字符的位置
next[j]=
- max{k|1<k<j,且从头开始的k-1个元素=j前面的k-1个元素} 当此集合非空时
- 0 当j=1时
- 1 其它情况
int Index_BF(SString S,SString T,int pos)
{
int i=pos;
int j=1;
while(i<S.length&&j<=T.length)
{
if(j==0||S.ch[i]==T.ch[i])//主串和子串依次匹配下一个字符
{
++i;
++j;
}
else
{
j=next[j];//i不变,j后退
}
if(j>T.length)
return i-T.length;//返回成功返回匹配的第一个字符的下标
else
return 0;//模式匹配不成功
}
}
- next[j]可以改进成nextval[j],不多介绍了
数组
- 数组:按一定格式排列起来的,具有相同类型的数据元素的集合
- 一维数组:若线性表中的数据元素为非结构的简单元素,则称为一维数组
- 一维数组的逻辑结构:线性结构。定长的线性表
- 声明格式:数据类型 变量名称[长度]
- 二维数组:若一维数组中的数据元素又是一维数组结构,则称为二维数组
- 二维数组的逻辑结构
非线性结构:每一个数据元素既在一个行表中,又在一个列表中
线性结构:该线性表的每个数据 元素也是一个定长的线性表 - 在C语言中,一个二维数组类型也可以定义为一维数组类型(其分量类型为一维数组类型)即:
typedef elemtype array2[m][n];
等价于typedef elemtype array1[n]; typedef array1 array2[m];
- 三维数组:若二维数组中的元素又是一个一维数组,则称作三维数组
- n维数组:若n-1维数组中的元素又是一个一维数组结构,则称作n维数组
- 结论:线性表结构是数组结构的一个特例,而数组结构又是线性表结构的扩展
- 数组特点:结构固定–定义后,维数和维界不再改变
- 数组的基本操作:除了结构的初始化和销毁之外,只有取元素和修改元素值的操作
- 数组一般都是采用顺序存储结构来表示
- 注意:数组可以是多维的,但存储数据元素的内存单元地址是一维的,因此,在存储数组结构之前,需要解决将多维关系映射到一维关系的问题
- 一维数组:LOC(i)=
1)LOC(0)=a, i=0;
2)LOC(i-1)+L=a+i*L,i>1 - 二维数组:以行序为主序
设数组开始存储位置LOC(0,0),存储每个元素都需要L个存储单元数组元素a[i][j]的存储位置是:LOC(i,j)=LOC(0,0)+(n*i+j)*L
特殊矩阵的压缩存储
- 矩阵:一个由m×n个元素排成的m行n列的表
- 矩阵的常规存储:
将矩阵表述为一个二维数组 - 矩阵的常规存储的特点:
可以对其元素进行随机存取
矩阵运算非常简单;存储的密度是1 - 不适宜常规存储的矩阵
值相同的元元素很多且呈某种规律分布;零元很多 - 矩阵的压缩存储:
为多个相同的非零元素只分配一个存储空间;对零元素不分配空间 - 特殊矩阵的压缩存储
1、什么是压缩存储?
若多个数据元素的值都相同,则只分配一个元素值的存储空间,且零元素不占存储空间
2、什么样的矩阵能够压缩?
一些特殊矩阵,如:对称矩阵,对角矩阵,三角矩阵,稀疏矩阵等
3、什么叫稀疏矩阵?
矩阵中非零元的个数较少(一般小于5%)
广义表
广义表(又称列表)是n>=0个元素的有限序列,其中每一个ai或者是原子,或者是一个广义表
- 广义表通常记作:LS=(a1,a2,…,an),其中:LS为表名,n为表的长度,小写字母表示原子
- 习惯上,一般用大写字母表示广义表,小写字母表示原子
- 表头:若LS非空(n>=1),则其第一个元素a1就是表头,记作head(LS)=a1;
注:表头可以是原子,也可以是子表 - 表尾:除表头之外的其它元素组成的表 记作 tail(LS)=(a2,…an)。
注:表尾不是最后一个元素,而是一个子表
广义表的性质
- 广义表中的数据元素有相对次序;一个直接前驱和一个直接后继
- 广义表的长度定义为最外层所包含元素的个数
- 广义表的深度定义为该广义表展开后所包含括号的重数
注意:原子的深度为0,空表的深度为1 - 广义表可以为其它广义表共享
- 广义表可以是一个递归的表
注意:递归表的深度是无穷值,长度是有限值 - 广义表是多层次结构,广义表的元素可以是单元素,也可以是子表,而子表的元素还可以是子表,。。。
广义表和线性表的区别
- 广义表可以看出是线性表的推广,线性表是广义表的特例
- 广义表的结构相当灵活,在某种前提下,他可以兼容线性表、数组、树和有向图等各种常用的数据结构
- 当二维数组的每行(或每列)作为子表处理时,二维数组即为一个广义表
- 另外,树和有向图也可以用广义表来表示
- 由于广义表不仅集中了线性表、数组、树和有向图等常见数据结构的特点,而且可有效的利用存储空间,因此在计算机的许多应用领域都有成功用广义表的实例
广义表的基本运算
- 求表头(GetHead(L)):非空广义表的第一个元素,可以是一个原子也可以是一个子表
- 求表尾(GetTail(L)):非空广义表除去表头元素以外其它元素构成的表。表尾一定是一个表