串,数组,广义表
串的定义
串(String): 零个或多个任意字符
组成的有限序列。
子串: 一个串中任意个连续字符组成的子序列(含空串)称为该串的子串。
主串: 包含子串的串相应地称为主串。
字符位置: 字符在序列中的序号为该字符在串中的位置。
子串位置: 子串第一个字符在主串中的位置。
空格串: 由一个或多个空格组成的串,与空串不同。
例:字符串a、b、c、d
a= D,
b= ‘JING'
C=‘BEIJING'
d=‘BEI JING'
------------
它们的长度是:3 4 7 8
c的子串是:a b
d的子串是:a b
a在c中的位置是:1
a在d中的位置是:1
b在c中的位置是:4
a在d中的位置是:
串的结构
逻辑结构
1.链式存储:链串
2.顺序存储:顺序串
- 顺序存储结构(更常用)
#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算法(特点:速度快,难理解)
1.BF算法
算法思想:
将主串的第n个字符和模式串的第一个字符比较,
- 若相等,继续逐个比较后续字符;
- 若不等,从主串的下一字符起,重新与模式串的第一个字符比较。
i=i-j+2 (回溯) (i:主串的位置)
j=1 (从头开始) (j:子串的位置)
- 直到主串的一个连续子串字符序列与模式串相等。返回值为S中与T匹配的子序列第一个字符的序号,即匹配成功。
- 否则,匹配失败,返回值0。
//算法的思路是从S的每一个字符开始依次与T的字符进行匹配。
S:a a a a b c d 主串:正文串
T:a b c 子串:模式
时间复杂度
则算法复杂度O(n*m)
比较差
2.KMP算法
算法思想
利用已经部分匹配的结果而加快模式串的滑动速度;且主串S的指针i不必回溯!时间复杂度可提速到 O(n+m)
可直接找next[j],它它表明当模式串中第j个字符与主串中相应字符不匹配时,在模式串中 ,需要和主串中该字符串进行比较的位置。
重要的例1
//KMP算法
int Index_KMP (SString s,SString T, int pos){
i= pos,j =1;
while (i<S.length && j<T.length) {
if (j==0 || S.ch[i]==T.ch[j]){ i++; j++; }
else j = next [j];//i不变,j后退
}
if (j>T.length) return i-T.length;/*匹配成功*/
else return 0;/*返回不匹配标志*/
}
//计算next函数值
void get_next(SString T, int &next[]){
i= 1; next[1] = o; j = O;
while( i<T.length){
if(j==O || T.ch[i] == T.ch[j]){
++i; ++j;
next[i] = j;
}
else
j = next[j];
}
}
重要的例2
根据next求nextval的值
模式串 | a | b | a | a | b | c | a | c |
---|---|---|---|---|---|---|---|---|
next值 | 0 | 1 | 1 | 2 | 2 | 3 | 1 | 2 |
nextval值 | 0 | 1 | 0 | 2 | 1 | 3 | 0 | 2 |
具体过程:
1.第一位的nextval值必定为0,第二位如果于第一位相同则为0,如果不同则为1。
2.第三位的next值为1,那么将第三位和第一位进行比较,均为a,相同,则第三位的nextval值为第一位的next值,为0。
3.第四位的next值为2,那么将第四位和第二位进行比较,不同,则第四位的nextval值为其next值,为2。
4.第五位的next值为2,那么将第五位和第二位进行比较,相同,第二位的next值为1,则继将第二位与第一位进行比较,不同,则第五位的nextval值为第二位的next值,为1。
5.第六位的next值为3,那么将第六位和第三位进行比较,不同,则第六位的nextval值为其next值,为3。
6.第七位的next值为1,那么将第七位和第一位进行比较,相同,则第七位的nextval值为0。7.第八位的next值为2,那么将第八位和第二位进行比较,不同,则第八位的nextval值为其next值,为2。
数组
数组的定义
数组: 按一定格式排列起来具有相同类型的数据元素的集合。
1.一维数组:
若线性表中的数据元素为非结构的简单元素,则称为一维数组。
一维数组的逻辑结构: 线性结构。定长的线性表。
声明格式:
数据类型 变量名称 [长度];
例: int num[5] = 10, 1,2,3,4};
2.二维数组:
若一维数组中的数据元素又是一维数组结构,则称为二维数组。
二维数组的逻辑结构
- 非线性结构:每一个数据元素既在一个行表中,又在一个列表中。
- 线性结构:该线性表的每个数据元素也是一定长的线性表。
二维数组的存储
二维数组可有两种存储方式:
- 以行序为主序;
- 以列序为主序。
行
列
以行序为主序:
设数组开始存储位置LOC( 0,0),存储每个元素需要L个存储单元数组元素 a[i][j]的存储位置是:LOC( i , j)= LOC(0,0)+(n*i+j)*L,也就是在a[i][j]前面所有元素个数。
总结
- 线性表结构是数组结构的一个特例,而数组结构又是线性表结构的扩展。
- 数组特点: 结构固定——定义后,维数和维界不再改变。
- 数组基本操作: 除了结构的初始化和销毁之外,只有取元素和修改元素值的操作。
注意: 数组可以是多维的,但存储数据元素的内存单元地址是一维的,因此,在存储数组结构之前,需要解决将多维关系映射到一维关系的问题。
例,有数组定义: int a[5];
每个元素占用4字节,假设a[0]存储在2000单元,a[3]地址是多少?
特殊矩阵的压缩存储
- 1.什么是压缩存储?
若多个数据元素的值都相同,则只分配一个元素值的存储空间,且零元素不占存储空间。 - 2.什么样的矩阵能够压缩?
一些特殊矩阵,如:对称矩阵,对角矩阵,三角矩阵,稀疏矩阵等。 - 3.什么叫稀疏矩阵?
矩阵中非零元素的个数较少(一般小于5%)
1.对称矩阵
特点: 在nxn的矩阵a中,满足如下性质:aij=aji (1 ≤i, j sn)
存储方法: 只存储下(或者上)三角(包括主对角线)的数据元素。共占用n(n+1)/2个元素空间。
2.三角矩阵
特点: 对角线以下(或者以上)的数据元素(不包括对角线)全部为常数c。
存储方法: 重复元素c共享一个元素存储空间,共占用n(n+1)/2+1个元素空间: sa[1… n(n+1)/2+1]。
3.对角矩阵
特点: 在nxn的方阵中,所有非零元素都集中在以主对角线为中心的带状区域中,区域外的值全为0,则称为对角矩阵。常见的有三对角矩阵、五对角矩阵、七对角矩阵等。
下图:一个7x7的三对角矩阵
例:
一个五对角矩阵, 以对角线顺序存储。
4.稀疏矩阵
有顺序存储和链式存储两种
1.三元组顺序表(顺序存储)
压缩存储原则:存各非零元的值、行列位置和矩阵的行列数。
注意:为更可靠描述,通常再加一个“总体”信息:即总行数、总列数、非零元素总个数(上图的 6 7 8)。
- 三元组顺序表的优点:非零元在表中按行序有序存储,因此便于进行依行顺序处理的矩阵运算。
- 三元组顺序表的缺点:不能随机存取。若按行号存取某一行中的非
零元,则需从头开始进行查找。
2.十字链表(链式存储)
- 优点:它能够灵活地插入因运算而产生的新的非零元素,删除因运算而产生的新的零元素,实现矩阵的各种运复
- 在十字链表中,矩阵的每一个非零元素用一个结点表示,该结点除了(row,col,value)以外,还要有两个域:
right: 用于链接同一行中的下一个非零元素; down: 用以链接同一列中的下一个非零元素。
例题
广义表
广义表的定义
- 广义表通常记作:Ls= (a, , …,an)
其中:LS为表名,n为表的长度,每一个ai为表的元素。 - 习惯上,一般用大写字母表示广义表,小写字母表示原子。
- 表头:若LS非空(n≥1),则其第一个元素a就是表头。
记作 head(LS)= a1。
注:表头可以是原子,也可以是子表。 - 表尾:除表头之外的其它元素组成的表。
记作tail(LS= (2 … an)。
注:表尾不是最后一个元素,而是一个子表。
例:p103
- 广义表中的数据元素有相对次序;一个直接前驱和一个直接
- 广义表的长度定义为最外层所包含元素的个数;
如:C=(a, (b, c))是长度为2的广义表。 - 广义表的深度定义为该广义表展开后所含括号的重数;
A =(b, o)的深度为1,B=(A,d)的深度为2,C=(f, B, h)的深度为3。注意:“原子”的深度为0;“空表”的深度为1。 - 广义表可以为其他广义表共享;如:广义表B就共享表A
在B中不必列出A的值,而是通过名称来引用,B= (A)。 - 广义表可以是一个递归的表。如:F=(a,F)=(a, (a, (a…)))
注意:递归表的深度是无穷值,长度是有限值。 - 广义表是多层次结构,广义表的元素可以是单元素,也可以
是子表,而子表的元素还可以是子表。
可以用图形象地表示。
例:D=(EF)
其中:E=(a, (b,c))
F=(d, (e))
广义表基本运算
- 求表头GetHead(L):非空广义表的第一个元素,可以是一个单原子,也可以是一个子表
- 求表尾GetTail(L):非空广义表除去表头元素以外其它元素所构成的表。表尾一定是一个广义表
例1:p103
例2: