题目
一个字符串类型的数组arr1,另一个字符串类型的数组arr2。arr2中有哪些字符,是arr1中
出现的请打印。arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印。arr2
中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印arr2中出现次数最大的前缀?
- 通过拆分字符串,每个字符串连接下一个集群,形成链路,每个节点通过pass属性记录经过该节点的次数,通过end属性记录以该节点结尾的路径的次数
- 如[“abc”,“ab”,“bc”,“bck”]四条路径,通过前缀树的方式进行记录
- 首先设置空节点,从空节点出发,空节点pass++,每次经过空节点,空节点pass都++
- 没有从空节点走到a,创建a节点,a节点pass++,等于1
- 从a走到b,没有从a节点走到b的路,创建b节点,b的pass++,等于1
- 从b走到c,没有从b节点走到c的路,创建c节点,c的pass++,等于1,且c结尾了,c的end++,等于1
- 下一条路,从ab出发
- 已有a,a节点的pass++,等于2
- 从a走到b,已有b,b的pass++,等于2,且b结尾,b的end++,等于1
- 下一条路从bc出发
- 没有从空节点到b的路,创建b节点,b的pass++,等于1
- 没有从b节点到c的路,创建c节点,c的pass++,等于1,且c结尾了,c的end++,等于1
- 下一条路从bck出发
- 已有空节点到b的路,b的pass++,等于2
- 已有b节点到c的路,c的pass++,等于2
- 没有c节点到k的路,创建k节点,k的pass++,等于1,且k结尾了,k的end++,等于1;
- 最终创建的路径图如下所示
- 为什么通过前缀树来记录,因为这种方式非常好求:有哪些字符是作为某个字符串前缀出现的、出现次数最大的前缀、出现的前缀次数、相关的问题。
class TreeNode {
pass = 0; //有多少个路径经过该节点
end = 0; //有多少个路径以该节点结尾
nexts = null; //存放TreeNode实例
//next[0] === null 没有走'a'的路
//next[0]!== null 有走'a'的路
//..
//next[25] !== null 有走'z'的路
}
class Tree {
root;
constructor() {
this.root = new TreeNode();
}
//前缀树插入路径
//word="abc"
insert(word) {
if (word === null) {
return;
}
let chs = word.spilt("");
let node = this.root;
node.pass++;
for (let i = 0; i < chs.length; i++) {
//获取字符对应的索引
let index = chs[i] - "a";
if (node.nexts[index] === null) {
node.nexts[index] = new TreeNode();
}
node = node.nexts[index];
node.pass++;
}
//最后一个结尾的节点end++
node.end++;
}
//查找路径出现过几次
search(word) {
if (!word) {
return 0;
}
let chs = word.spilt("");
let node = this.root;
for (let i = 0; i < chs.length; i++) {
let index = chs[i] - "a";
//有一个路径节点不存在,说明该路径不存在
if (node.nexts[index] === null) {
return 0;
}
node = node.nexts[index];
}
//在上面基础上,返回以路径最后一个节点结尾的记录的end数值
return node.end;
}
//查找给定路径为前缀的数量
searchPrefix(word) {
if (!word) {
return 0;
}
let chs = word.spilt("");
let node = this.root;
for (let i = 0; i < chs.length; i++) {
let index = chs[i] - "a";
//有一个路径节点不存在,说明该路径不存在
if (node.nexts[index] === null) {
return 0;
}
node = node.nexts[index];
}
//在上面基础上,返回路径最后一个节点记录的经过次数pass数值
return node.pass;
}
//删除路径
delete(word) {
//确定存在路径才删除
if (!this.search(word) || !word) {
return;
}
let chs = word.spilt("");
let node = this.root;
for (let i = 0; i < chs.length; i++) {
let index = chs[i] - "a";
//删除过程中有一个路径节点经过次数为0了,无需继续查找后续路径了,直接置空
if (--node.nexts[index].pass === 0) {
node.nexts[index] = null;
}
node = node.nexts[index];
}
//以该路径结尾的记录数值-1
node.end--;
}
}