前缀树--记录路径

题目

一个字符串类型的数组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;
  • 最终创建的路径图如下所示
  • 为什么通过前缀树来记录,因为这种方式非常好求:有哪些字符是作为某个字符串前缀出现的、出现次数最大的前缀、出现的前缀次数、相关的问题。

前缀树--记录路径_第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--;
  }
}

你可能感兴趣的:(前端算法,数据结构)