自学内容网 自学内容网

前缀树介绍

数风流人物,还看今朝!

前缀树

Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。

前缀树,也叫做字典树(Trie),是一种专门处理字符串数据的数据结构。它特别适合用来处理和查询大量的字符串,比如词汇表、域名等。

想象一下,你有一大堆单词,你想快速地查找某个单词是否存在,或者找到以某个前缀开始的所有单词。用普通的列表或者哈希表来做这些操作可能不太高效,尤其是当数据量很大时。这时,前缀树就能派上用场了。

前缀树的基本思想是利用字符串的公共前缀来减少查询的时间。它就像一棵倒立的树,每个节点代表一个字符。从根节点开始,每条边代表一个字符,通向下一个节点。这样,整个路径就组成了一个字符串。

举个简单的例子,假设有以下几个单词:cat, car, dog, dart,dac

这样,每个单词都有一条从根节点到叶节点的路径,路径上的字符连起来就是这个单词。

前缀树的厉害之处在于,它可以非常快速地查找以某个前缀开始的单词。比如,如果你想找出所有以 ‘ca’ 开头的单词,你只需要从根节点沿着 ‘c’ 和 ‘a’ 的边走下去,然后收集所有从那个节点出发的路径,就能得到 ‘cat’ 和 ‘car’。

此外,前缀树还可以用来检查一个单词是否已经存在。你只需要沿着单词的每个字符走路径,看是否能到达一个表示单词结束的节点。

请你实现 Trie 类:

  • Trie() 初始化前缀树对象。

  • void insert(String word) 向前缀树中插入字符串 word

  • boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false

  • boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false

这是208. 实现 Trie (前缀树)

struct Node {
    vector<Node*>son;
    bool end ; // 创建一个bool值的end,代表是否是终止节点
    Node():son(26,NULL),end(false){}
};
class Trie {
    Node* root = new Node(); // 创建一个根节点
    // 返回三种数字,0,1,2
    // 0代表没有这个单词
    // 1代表有这个单词,但不是结尾
    // 2代表有这个单词,且是结尾
    int find(string word) {
        auto cur = root; // 遍历字符串 word,同时用一个变量 cur 表示当前在 26
                          // 叉树的哪个节点,初始值为 root。
        for (auto c : word) {
            c -= 'a';
            if (cur->son[c] == nullptr) {
                return 0;
            } // 如果 word[i] 不是 cur 的儿子,返回 0。search 和
            // startsWith 收到 0 之后返回 false。
            cur = cur->son[c]; // 更新 cur 为儿子列表中的相应节点。
        }
        // 如果cur->end为true,说明有这个单词,且是结尾
        // 如果cur->end为false,说明有这个单词,但并不是结尾,中间就结束了
        return cur->end ? 2 : 1;
    }

public:
    void insert(string word) {
       auto cur =root; // 变量 cur 表示当前在 26 叉树的哪个节点,初始值为 root。
        for (auto c : word) {
            c -= 'a';
            // 如果之前没有就新建一个节点
            if (cur->son[c] == nullptr) {
                cur->son[c] = new Node();
            }
            cur = cur->son[c]; // 更新 cur 为儿子列表中的相应节点。
        }
        cur->end = true; // 把end置为空
    }
    // search:如果字符串word已经在前缀树当中,返回true
    bool search(string word) { 
        return find(word) == 2; 
    }
    // startwith:如果前缀prefix已经在前缀树当中,返回true
    bool startsWith(string prefix) {
        // 是1,2都行,0不行
        return find(prefix) != 0;
    }
};

很遗憾,在力扣里面是付费的,牛客里面不付费。

这个是1804.实现前缀树II,这里和上面的不同在于,一个是判断word单词是否出现,一个是查询前缀树里,word单词出现了几次,一个是查询前缀树里,是否有单词以pre做前缀,一个是一个是查询前缀树里,有多少单词以pre做前缀

此时的节点是维护pass,end,和nexts数组,如果有节点经过,pass++,一个单词结束end++,维护这两个信息。

// 测试链接 : https://leetcode.cn/problems/implement-trie-ii-prefix-tree/
public class Code01_TrieTree {

 // 路是数组实现的
 class Trie{

  class TrieNode {
   public int pass;
   public int end;//单词的结尾
   public TrieNode[] nexts;

   public TrieNode() {
    pass = 0;
    end = 0;
    nexts = new TrieNode[26];
   }
  }

  private TrieNode root;

  public Trie() {
   root = new TrieNode();
  }

  public void insert(String word) {
   TrieNode node = root;
   node.pass++;
   for (int i = 0, path; i < word.length(); i++) { // 从左往右遍历字符
    path = word.charAt(i) - 'a'; // 由字符,对应成走向哪条路
    if (node.nexts[path] == null) {
     node.nexts[path] = new TrieNode();
    }
    node = node.nexts[path];
    node.pass++;
   }
   node.end++;
  }

  // 如果之前word插入过前缀树,那么此时删掉一次
  // 如果之前word没有插入过前缀树,那么什么也不做
  public void erase(String word) {
   if (countWordsEqualTo(word) > 0) {
    TrieNode node = root;
    node.pass--;
    for (int i = 0, path; i < word.length(); i++) {
     path = word.charAt(i) - 'a';
     if (--node.nexts[path].pass == 0) {
      node.nexts[path] = null;
      return;
     }
     node = node.nexts[path];
    }
    node.end--;
   }
  }

  // 查询前缀树里,word单词出现了几次
  public int countWordsEqualTo(String word) {
   TrieNode node = root;
   for (int i = 0, path; i < word.length(); i++) {
    path = word.charAt(i) - 'a';
    if (node.nexts[path] == null) {
     return 0;
    }
    node = node.nexts[path];
   }
   return node.end;
  }

  // 查询前缀树里,有多少单词以pre做前缀
  public int countWordsStartingWith(String pre) {
   TrieNode node = root;
   for (int i = 0, path; i < pre.length(); i++) {
    path = pre.charAt(i) - 'a';
    if (node.nexts[path] == null) {
     return 0;
    }
    node = node.nexts[path];
   }
   return node.pass;
  }

 }

原文地址:https://blog.csdn.net/m0_75266675/article/details/144755733

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!