Trie树(字典树)

一、Trie树

Trie树,也叫字典树,是一种专门用来处理字符串匹配的树形结构,用来解决在一组字符串集合中快速查找某个字符串的问题

Trie树可以最大限度地减少无谓的字符串比较,查询效率比哈希表高

Trie树的本质就是利用字符串之间的公共前缀,将重复的前缀合并在一起

举个例子,有6个字符串,分别是:how、hi、her、hello、so、see。对这6个字符串做一下预处理,组成Trie树的结构,每次查找字符串都是在Trie树中进行匹配查找,构造出来的就是下图中的样子

在这里插入图片描述

Trie树的基本性质:

  • 根节点不包含字符,除根节点以外每个节点只包含一个字符
  • 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串
  • 每个节点的所有子节点代表的字符都不相同

构造Trie树分解过程如下图,构造过程的每一步都相当于往Trie树中插入一个字符串。当所有字符串都插入完成之后,Trie树就构造好了

在这里插入图片描述
在这里插入图片描述

当在Trie树中查找一个字符串,比如查找字符串her,将要查找的字符串分割成单个的字符h、e、r,然后从Trie树的根节点开始匹配。如下图所示,绿色的路径就是在Trie树中匹配的路径

在这里插入图片描述

如果要查找的是字符串he,用上面同样的方法,从根节点开始,沿着某条路径来匹配,如图所示,绿色的路径,是字符串he匹配的路径。但是,路径的最后一个节点e并不是红色的。也就是说,he是某个字符串的前缀子串,但并不能完全匹配任何字符串

在这里插入图片描述

构建Trie树的过程,需要扫描所有的字符串,时间复杂度是 O ( n ) O(n) O(n)(n表示所有字符串的长度和)。每次查询时,如果要查询的字符串长度是k,那只需要比对大约k个节点,就能完成查询操作,时间复杂度为 O ( k ) O(k) O(k)(k表示要查找的字符串的长度)

二、LeetCode208:实现Trie(前缀树)

Trie树的结点结构:

Trie树是一个有根的树,其结点具有以下字段:

  • 最多R个指向子结点的链接,其中每个链接对应字母表数据集中的一个字母(此题中R为26)
  • 布尔字段,以指定节点是对应键的结尾还是只是键前缀

Trie树的存储:
在这里插入图片描述

单词leet在Trie树中的表示:

在这里插入图片描述

题解:

class Trie {
    class TrieNode {
        char data;
        TrieNode[] children = new TrieNode[26];
        boolean isEnd = false;

        TrieNode(char data) {
            this.data = data;
        }

        boolean hasNode(char node) {
            return children[node - 'a'] != null;
        }

        TrieNode findNode(char node) {
            return children[node - 'a'];
        }

        TrieNode createNode(char node) {
            children[node - 'a'] = new TrieNode(node);
            return children[node - 'a'];
        }

    }

    TrieNode head;

    public Trie() {
        head = new TrieNode('-');
    }

    public void insert(String word) {
        TrieNode index = head;
        for (char c : word.toCharArray()) {
            if (index.hasNode(c))
                index = index.findNode(c);
            else
                index = index.createNode(c);
        }
        index.isEnd = true;
    }
    
    public boolean search(String word) {
        TrieNode index = head;
        for (char c : word.toCharArray()) {
            if (index.hasNode(c))
                index = index.findNode(c);
            else
                return false;
        }
        return index.isEnd;
    }
    
    public boolean startsWith(String prefix) {
        TrieNode index = head;
        for (char c : prefix.toCharArray()) {
            if (index.hasNode(c))
                index = index.findNode(c);
            else return false;
        }
        return true;
    }
}

三、LeetCode212:单词搜索 II

给定一个二维网格board和一个字典中的单词列表words,找出所有同时在二维网格和字典中出现的单词

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用

示例:

输入: 
words = ["oath","pea","eat","rain"] and board =
[
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]
输出: ["eat","oath"]

题解:

    public List<String> findWords(char[][] board, String[] words) {
        Trie trie = new Trie();
        for (String word : words) {
            trie.insert(word);
        }
        int m = board.length;
        int n = board[0].length;
        boolean[][] visited = new boolean[m][n];
        Set<String> resultSet = new HashSet<>();
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                helper(board, visited, trie.root, i, j, m, n, resultSet);
            }
        }
        return new ArrayList<>(resultSet);
    }

    private void helper(char[][] board, boolean[][] visited, TireNode node, int i, int j, int m, int n, Set<String> resultSet) {
        if (i < 0 || j < 0 || i >= m || j >= n || visited[i][j])
            return;
        node = node.children[board[i][j] - 'a'];
        if (node == null)
            return;
        if (node.word != null)
            resultSet.add(node.word);
        visited[i][j] = true;
        helper(board, visited, node, i + 1, j, m, n, resultSet);
        helper(board, visited, node, i - 1, j, m, n, resultSet);
        helper(board, visited, node, i, j + 1, m, n, resultSet);
        helper(board, visited, node, i, j - 1, m, n, resultSet);
        visited[i][j] = false;
    }

    class Trie {
        TireNode root = new TireNode();

        void insert(String word) {
            TireNode node = root;
            for (char c : word.toCharArray()) {
                if (node.children[c - 'a'] == null)
                    node.children[c - 'a'] = new TireNode();
                node = node.children[c - 'a'];
            }
            node.word = word;
        }
    }

    class TireNode {
        TireNode[] children = new TireNode[26];
        String word = null;
    }

常用数据结构的时间、空间复杂度:
在这里插入图片描述

https://www.bigocheatsheet.com/

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单的 C 语言字典 Trie 的代码实现: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_WORD_LEN 100 typedef struct TrieNode { char value; struct TrieNode *children[26]; int is_end; } TrieNode; TrieNode *createNode(char value) { TrieNode *node = (TrieNode *) malloc(sizeof(TrieNode)); node->value = value; for (int i = 0; i < 26; i++) { node->children[i] = NULL; } node->is_end = 0; return node; } void insert(TrieNode *root, char *word) { TrieNode *curr = root; int len = strlen(word); for (int i = 0; i < len; i++) { int index = word[i] - 'a'; if (curr->children[index] == NULL) { curr->children[index] = createNode(word[i]); } curr = curr->children[index]; } curr->is_end = 1; } int search(TrieNode *root, char *word) { TrieNode *curr = root; int len = strlen(word); for (int i = 0; i < len; i++) { int index = word[i] - 'a'; if (curr->children[index] == NULL) { return 0; } curr = curr->children[index]; } return curr->is_end; } int main() { TrieNode *root = createNode('\0'); char word[MAX_WORD_LEN]; int choice = 0; do { printf("1. Insert Word\n"); printf("2. Search Word\n"); printf("3. Exit\n"); printf("Enter Choice: "); scanf("%d", &choice); switch (choice) { case 1: printf("Enter Word to Insert: "); scanf("%s", word); insert(root, word); break; case 2: printf("Enter Word to Search: "); scanf("%s", word); if (search(root, word)) { printf("%s is present in the dictionary.\n", word); } else { printf("%s is not present in the dictionary.\n", word); } break; case 3: printf("Exiting...\n"); break; default: printf("Invalid Choice!\n"); break; } } while (choice != 3); return 0; } ``` 该实现使用了一个 TrieNode 结构体来表示 Trie 的每个节点,其包含了节点的值,子节点指针数组和一个标志位,用于指示该节点是否为单词的结尾。 在插入单词时,从根节点开始遍历 Trie ,如果当前节点的相应子节点为空,则新建一个节点并将其作为当前节点的相应子节点。最后将单词的结尾节点的标志位设置为 1。 在查找单词时,同样从根节点开始遍历 Trie ,如果当前节点的相应子节点为空,则说明该单词不存在于 Trie 。如果单词的最后一个字符所在的节点的标志位为 1,则说明该单词存在于 Trie 。 该实现还包含了一个简单的命令行界面,用于接收用户的输入并执行相应的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邋遢的流浪剑客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值