原文章:
数据结构学习记录:第四章:串 - 知乎 (zhihu.com)
此文章为作者本人搬运至该网站。
说在前头:
各位在学习过程中,如果有任何不懂的地方,可以随时评论或者私信我!!!我每天都在高强度网上冲浪(这一点真的属实),从各位进行评论,到我发现评论,我认为应该最多不超过半个小时,所以啊,各位,尽情用你们的评论和私信淹没我吧!(=・ω・=)
在学习串之前,我们一定已经接触过一个名字类似的内容——字符串,以及其的一些用法,
而在这里,看书上那么繁琐而又奇葩的内容,你是不是又被绕晕了?
其实那些内容就是把原来字符串的东西换了个说辞又写了一遍!
现在我把书上的内容过滤掉一遍,再看应该就会清晰许多。
1.串是什么:
这里就不用我讲了吧?目前在我看来,其实就和字符串,也就是字符数组,大同小异罢了,
而我觉得,串里面的许多函数,基本上一半都能用#include <string.h>里面的函数来解决。
2.定长顺序串的表示:
按照课本的内容来说,串有定长顺序存储和堆分配存储,以及块链存储,
而在我对课本翻来翻去,并请教了诸多同学后,发现 定长顺序存储 不仅用处不大,而且课本内容还说得云里雾里,不明所以,
于是在这里我就简单提一下,并说一下为什么我认为课本没讲清楚:
从存储表示我们可以看出,串的定长存储最大为255,而为什么要在前面加个unsigned呢,因为char的数据范围是从-128到+127,如果改成unsigned,那么就成了0到255,可能是为了扩大存储范围吧。
而这个typedef是老朋友了,通过这个可以定义串,就是比如SString T,就相当于char T[255](这个应该大家都知道了罢),
那么怎么表示串的长度的呢?课本是这样描述的:
其中课本用的是第一个,
用S1[0]表示串的长度,
但是!!!
你也没告诉我怎么得出来S1[0]啊???而且S1[0]不应该和后面的数据一样是char类型的吗,为什么还能和define出的int类型的MAXSTRLEN相比较呢???这能出结果吗???
得知这里的内容的时候我是一脸懵b的,
为什么不用string库里面的strlen函数呢?
这里同理,
这里是求S的字串Sub,最后的
Sub[0] = len
一个int类型的数赋值给char类型。。。。。。
所以我认为这里不需要过多计较,
而且重要的是,我看别的博主的关于串的教程,除了那些直接照抄课本的水博客,基本上都没有关于这个S1[0]的内容,所以,直接略过就好~
(帮大家避雷了,不愧是我(叉腰))
3.堆排序存储表示:
现在才是正菜!
这样看的话,其实和之前内容没什么差别,就是指针指向的数组,用malloc动态分配内存。
(1).生成串:
不过这里却不是直接用malloc创建新的串,而是把一个chars指针指向的串赋给了T,这个函数虽然简单,但由于之前没有见过,我还是讲一下吧:
第一个if语句,不用多说,如果这个T有数据,那就释放再创建个新的,
下面这个for循环可就有来头了!仔细看,
for (i = 0, c = chars; *c; ++i, ++c);
这个for循环最后有一个分号!!!
这绝对不是写错了,而是有bear而来(
如果对for循环烂熟于心的话,其实就会了解到,其实第二个分号后面的内容也可以放在for循环大括号内,那么这样就实现了一个独立的for循环,而中间那个*c我个人认为是判断c指向的地方有没有数据,也就相当于
*c != '\0'
而如果是指针的形式,应该也可以写成 c != NULL,
(在这里我要提示一下,NULL只可以用作对指针的判断,不能对数组元素判断(当年的痛))
如果把这个for循环改成while循环,就是这样:
i = 0;
c = chars;
while ( *c != '\0' ){
i++;
c++;
}
而后面的内容应该也挺简单的,无非就是分配空间,数据更新之类的,
不过这个。。。。。
T.ch[0...i-1] = chars[0...i-1];
其实是个伪代码,就是把数据一个个传过去,换成c语言就是:
for (int j = 0; j < i; j++){
T.ch[j] = chars[j];
}
这样子偷懒是吧。。。。。
(2).比较串和清空串:
(3).返回字串:
这些都比较简单,应该自己都能看懂吧~~
如果实在看不懂或者有什么地方有疑问也可以来私信问我~~
4.串的块链存储表示
和线性表的链式存储结构相类似,也可采用链表方式存储串的值,也就是说,每一个串都是链表的节点,如图:
添加图片注释,不超过 140 字(可选)
CNUNKSIZE就是每个节点的串的长度,最后再有个指向下一个节点的指针。
不过毕竟这里不是重点内容(老师连pta作业都没有留串的内容),所以就简单介绍一下。
5.串的模式匹配算法:
接下来更是重量级!!!
串的模式匹配一般用于求字串位置的定位,也就是求字串在主串中的位置(找到主串中某一部分的从头到尾都和字串一模一样且字符连续的),首先用的就是普通匹配算法:
这里还是用的是定长顺序串的模式,下面改成堆排序的方式:
int Index(Htring S, HString T,int pos) {
// 返回子串T 在主串 S中第 pos个字符之后的位置。若不存在,则函数值为0
//其中,T非空,1 <= pos <= StrLength(S)。
i = pos - 1;
j = 0;
while(i <= S.length && j <= T.length) {
if(S.ch[i] == T.ch[j]) { // 继续比较后继字符
++i;
++j;
} else {
i = i - j + 1; //指针后退重新开始匹配
j = 0;
}
}
if(j > T.length)
return i - T.length + 1;
else
return 0;
}// Index
(i和j都减去了1是因为数组的特性,a[0])这里我们以这个例子来讲:
找到在主串第2(pos)个字符之后字串DEF的位置,这里我把其他字符都简化为同一个字符Z,并且中间加了空格(空格不需要判断),
pos值为2,那么i的值就为1,在主串中就是第二个Z的位置,
(这里的箭头不是指针啊喂!只是个提示)
之后进入while循环,if判断,显然两个不一样,那么就走else,
也许有人对这个指针后退重新匹配,这个i = i - j +1;不是很理解,我们先直接带入,这里i是1,j是0,减完之后,i变成了2,j还是0,也就是说这个指针后退其实是针对j的,
而那个i = i - j +1;我们先按下不表,继续循环,显然这个还是不一样,继续i = i - j +1,
直到这里,再进行判断,两个字符终于相等了!那么就把i和j都前进一格:
E同理:
但是在这里,两个又不一样了!
那么就走else,i = i - j +1,
这里是不是看明白了?
其实else判断的就是直到主串中找到和子串匹配的第一个字符,之后i和j同时自加,如果最后不行的话,那就返回到找到第一个字符的地方,再给i加个1,让i从这个地方再继续往下找和子串匹配的第一个字符,直至全部匹配为止。
这样不断类推,那就实现了定位函数。
在这种情况下,算法的时间复杂度为O(n+m),其中n和m分别为主串和子串的长度(来源课本),
但是在有些情况下,该算法的效率却很低。比如子串为'00000001',而主串为'000000000000000000000000000000000000000000000000000001'(课本上比这个还多。。。。。),
由于子串的前七个字符均为'0',又主串中前52个字符也均为'0',一开始的每一次比较都会在子串的最后一个字符出现不相同,这样才讲指针i回溯到i-6的位置上,并且还要重新比较8次。在整个匹配过程中,指针i一共要回溯45次,一共的while循环次数为46 * 8(index * m)为368次!!!
那么这样,该算法在最坏情况下的时间复杂度为O(n*m),还是挺麻烦的。
而我们也可以看出,有很多地方其实是不需要进行匹配的,
所以下一步,我们带来一个新算法——KMP算法。
6.KMP算法:
。。。
。。。
。。。
崩溃了家人们,我今天从早上看到晚上,还是没看懂。。。。。
这里是我搜索的记录TAT
学习的过程也是极为痛苦,经历了建立认知——发现不对——残忍打破认知体系——重新认知这些痛苦过程
我先放出这些链接,等我回头想起来了再看吧。。。。或者你们也可以看这些内容,反正里面每一篇文章都有人评论说醍醐灌顶。。。
从头到尾彻底理解KMP(2014年8月22日版)_kmp算法 csdn-CSDN博客
KMP算法——通俗易懂讲好KMP算法:实例图解分析+详细代码注解 --》你的所有疑惑在本文都能得到解答_kmp算法例子-CSDN博客
而压垮我想要搞懂kmp算法的最后一根稻草是什么呢。。。
是我同学告诉我这个地方我们老师说过让自己理解!!!
也就是说老师都不想讲!!!
而且我还落下了那么多课,PTA页不布置串的题目,还钻这个kmp算法干什么!!!
所以我就先挖个坑吧。。。。等什么时候有功夫钻研了,或者有高人指点之后我再写吧。。。
下面是我之前尝试写出来的东西,不过也烂尾了,建议先不要看了。。。
此时此刻我的心情
真的很抱歉。。。。
但是如果我的学习效率很高的话,我也不至于去写这系列文章了。。。。。