数据结构Javascript实现 | 字典树 trie

概念

字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。

图示

在这里插入图片描述

代码

字典树节点

class TrieNode {
  constructor(key) {
    this.key = key;
    this.children = [];
  }
}

字典树

class Trie {
  constructor() {
    this.root = new TrieNode(null);
  }

  /** 插入 */
  insert(string) {
    this._insert(string, this.root);
  }

  /**
   * 插入数据到指定节点
   * @param {string} string 数据
   * @param {TrieNode} node 操作节点
   */
  _insert(string, node) {
    if (!string) {
      return;
    }
    let children = node.children;
    let header;
    // 遍历子节点,存在与第一个字符相等的节点赋值为 header
    children.map(child => {
      if (child.key === string[0]) {
        header = child;
      }
    })
    if (header) {
      // 递归:除了第一个字符外的其他字符插入
      this._insert(string.substring(1), header);
    } else {
      let node = new TrieNode(string[0])
      if (!children.length) {
        // 操作节点无子节点时,直接将操作节点push到子节点中
        children.push(node);
      } else {
        // 否则,对比操作节点与子节点的ASCII码,按序插入到对应位置
        let position = 0;
        children.map(child => {
          if (child.key < string[0]) {
            position++;
          }
        })
        children.splice(position, 0, node);
      }
      this._insert(string.substring(1), node);
    }
  }

  /** 查找树中是否有string */
  search(string) {
    if (string === '' || !this.root.children.length) {
      return false;
    }
    for (let i = 0; i < this.root.children.length; i++) {
      if (this._search(string, this.root.children[i])) {
        return true;
      }
    }
    return false;
  }

  /**
   * 查找某数据是否在指定节点中
   * @param {string} string 
   * @param {TrieNode} node 
   */
  _search(string, node) {
    if (node.key !== string[0]) {
      return false;
    }
    let children = node.children;
    // 无子节点的key值与string相同
    if (!children.length && string.length === 1) {
      return true;
    }
    if (children.length > 0 && string.length > 1) {
      // 递归查询
      for (let i = 0; i < children.length; i++) {
        if (children[i].key === string[1]) {
          return this._search(string.substring(1), children[i]);
        }
      }
    } else {
      return false;
    }

  }

  delete(string) {
    if (this.search(string)) {
      for (let i = 0; i < this.root.children.length; i++) {
        if (this.root.children[i].key === string[0]) {
          this._delete(string, this.root, i, string)
          return true;
        }
      }
    } else {
      return false;
    }
  }

  /**
   * @param {string} string 
   * @param {TrieNode} parent 
   * @param {number} index 
   * @param {string} originString 
   */
  _delete(string, parent, index, originString) {
    // key值与string[0]相等的节点
    let node = parent.children[index];
    let children = node.children;
    if (!children.length && string.length === 1) {
      // string和node.key相等 且 node无子节点
      parent.children.splice(index, 1);
      return this.delete(originString.substring(0, originString.length - 1))
      // this.delete(delStr.substring(0, delStr.length - 1))
    } else if (children.length && string.length > 1) {
      for (let i = 0; i < children.length; i++) {
        if (children[i].key === string[1]) {
          this._delete(string.substring(1), node, i, originString);
          return true
        }
      }
    }
    return false;
  }

  show() {
    this._show(this.root);
  }

  _show(node) {
    node.children.map(child => {
      console.log('parent: ' + node.key + '; node:', child);
      if (child.children.length) {
        this._show(child);
      }
    })
  }
}

示例

const tree = new Trie();
tree.insert('girl');
tree.insert('boy');
tree.insert('book');
tree.insert('go');

console.log(tree.search('b')); // false
console.log(tree.search('boy')); // true

tree.delete('go')

console.log(tree.root);
tree.show();
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值