串、数组与广义表
文章目录
计算机上的非数值处理的对象大部分是字符串数据,字符串一般简称为串。串是一种特殊的线性表,其特殊性体现在数据元素是一个字符,也就是说,串是一种内容受限的线性表。由于现今使用的计算机硬件结构是面向数值计算的需要而设计的,在处理字符串数据时比处理整数和浮点数要复杂得多。而且,在不同类型的应用中,所处理的字符串具有不同的特点,要有效地实现字符串的处理,就必须根据具体情况使用合适的存储结构。
1.串的基本概念
1.1串的定义
串(string)(或字符串) 是由零个或多个字符组成的有限序列, 一般记为:
s= "a1 a2 … an " (n>=0)
其中,s是串的名, 用双引号括起来的字符序列是串的值;ai(1<=i<=n)可以是字母、数字或其他字符;串中字符的数目n称为串的长度。零个字符的串称为空串(null string) , 其长度为零。串中任意个连续的字符组成的子序列称为该电的子串。
1.2其他概念
概念 | 定义 |
---|---|
子串 | 串中任意个连续字符组成的子序列称为该串的子串 |
主串 | 包含子串的串相应地称为主串 |
字符位置 | 字符在序列中的序号为该字符在串中的位置 |
子串位置 | 子串第个字符在主串中的位置(注:原文中此处可能有遗漏) |
空格串 | 由一个或多个空格组成的串,与空串不同 |
串相等 | 当且仅当两个串的长度相等并且各个对应位置上的字符都相同时,这两个串才是相等的。 |
2.串的类型定义、存储结构及运算
2.1串的类型定义
2.1.1串的顺序存储结构
#define Max_Size 255
typedef int ElemType;
typedef struct {
ElemType data[Max_Size + 1]; //这里是方便理解从1开始存储
int length;
}SString;
2.1.2串的链式存储结构
//链式存储
#define CHUCKSIZE 100 //存储容量可以由用户自己定义
typedef struct Chuck {
char ch[CHUCKSIZE];
struct Chuck* next;
}Chuck;
typedef struct {
Chuck* head;
Chuck* tail; //串的头指针和尾指针
int curlen; //串的当前长度
}LString;
2.2串的模式匹配算法
2.2.1算法目的:
确定主串中所含子串(模式串)第一次出现的位置(定位)
2.2.2算法应用:
搜索引擎、拼写检查、语言翻译、数据压缩
2.2.3算法种类:
BF算法(朴素的、穷举的)(Brute-Force,又称古典的、经典的)
KMP算法(特点:速度快)
2.2.4串的模式匹配算法----BF算法
Brute-Force简称为BF算法,亦称简单匹配算法。采用穷举法的思想。
BF算法设计思想——Index(S,T,pos)
- 将主串的第pos个字符和模式串的第一个字符比较
- 若相等,继续逐个比较后续字符,
- 若不等,从主串的下一字符起,重新与模式串的第一个字符比较
代码实现
//BF算法
int Index_BF(SString S,SString T,int pows) {
int i = pows;
int j = 1;
while (i <= S.length && T.length) {
if(S.data[i] == T.data[j]) {
i++;
j++;
}
else {
i = i - j + 2;
j = 1;
}
if(j > T.length) {
return i - T.length;
}
else {
return 0;
}
}
}
2.2.5KMP(Knuth Morris Pratt)算法
KMP算法是D.E.Knuth、J.H.Morris和V.R.Pratt共同提出的,简称KMP算法。
该算法较BF算法有较大改进,从而使算法效率有了某种程度的提高。
KMP算法的设计思想:
利用已经部分匹配的结果而加快模式串的滑动速度?且主串S的指针i不必回溯!可提速到O(n+m)!
为此,定义next[]函数,表明当模式中第个字符与主串中相应字行“失配”时,在模式中需重新和主串中该字符进行比较的字符的位置。
代码实现
//求next数组
void GetNext(SString T, int *next) {
next[1] = 0;
int j = 0;
int i = 1;
while (i < T.length) {
if (0 == j || T.data[i] == T.data[j]) {
i++, j++;
next[i] = j;
} else {
j = next[j];
}
}
}
int Index_KMP(SString S, SString T, int pos) {
int *next = (int *)malloc(sizeof(int) * (T.length + 1));
GetNext(T, next);
int i = pos;
int j = 1;
while (i <= S.length && j <= T.length) {
if(j == 0 || S.data[i] == T.data[j]) {
i++;
j++;
}
else {
j = next[j];
}
}
if(j > T.length) {
return i - T.length;
}
else {
return 0;
}
}
3.数组的基本概念
概念 | 描述 |
---|---|
数组 | 按一定格式排列起来的具有相同类型的数据元素的集合。 |
维数组 | 若线性表中的数据元素为非结构的简单元素,则称为一维数组。 |
一维数组的逻辑结构 | 线性结构。定长的线性表 |
声明格式 | 数据类型 变量名称[长度]; |
4.广义表的基本概念
广义表(又称列表 Lists)是 n≥0 个元素 a, a.,… an-1的有限序列,其中每一个 a;或者是原子,或者是一个广义表。
概念 | 描述 |
---|---|
表头 | 若线性表LS非空(n≥1),则其第一个元素a就是表头。记作head(LS): |
表头注解 | 表头可以是原子,也可以是子表。 |
表尾 | 除表头之外的其他元素组成的表。记作tail(LS) = (a2… an)。 |
表尾注解 | 表尾不是最后一个元素,而是一个子表。 |
5.广义表的性质
- 元素次序:广义表中的元素有相对次序,每个元素有一个直接前驱和一个直接后继。
- 长度定义:广义表的长度定义为最外层所包含的元素个数。
- 深度定义:
- 广义表的深度定义为该广义表展开后所含括号的层数。
- 例如:
- A = (b, ℃) 深度为 1
- B = (A, d) 深度为 2
- C = (f, B, h) 深度为 3
- 注意:原子的深度为 0,空表的深度为 1。
- 共享性:在广义表中,一个元素可以为其他广义表共享,如上述广义表 B 就共享了 A。
- 递归性:
- 广义表可以是一个递归的表,如上述的 F 广义表。
- 注意:递归表的长度是有限的,但是深度是无限的。
6.广义表的基本运算
广义表 | 基本运算 | 结果 |
---|---|---|
D | GetHead | E |
D | GetTail | (F) |
E | GetHead | α |
E | GetTail | ((b, c)) |
((b, c)) | GetHead | (b, c) |
((b, c)) | GetTail | () |
(b, c) | GetHead | b |
(b, c) | GetTail | © |
c | GetHead | c |
c | GetTail | () |