前缀树介绍及代码实现

1. 前缀树介绍

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

前缀树,就是根据字符串中的每一个字符来到达相应的结点,所以比较适合统计或者查找字符串。如果使用数组存储字符串的话,每次统计或者查找,都要遍历整个数组,而是用前缀树存储字符串,只需要根据字符串中的每个字符找,时间复杂度和要查找的字符串有关,和字符串的数量无关。另外,通过前缀树,还可以非常快捷的查以xxx开头的字符串个数,如果想利用前缀树实现其他功能,可以在节点中添加其他属性。

2. 前缀树结构

比如使用前缀树,存储字符串"ewen"
在这里插入图片描述
如果要存储其他的字符串,就从根节点开始根据字符串中的字符找,如果能根据字符找到结点就继续往下找,找不到,就新建边和结点,比如再存储字符串"ewmm"
在这里插入图片描述

3. 前缀树代码实现

public class Code_01_TrieTree {
    //定义前缀树结点
    public static class TrieNode{
        //代表有多少字符串经过这个结点
        public int pass;
        //代表有多少个字符串以这个结点结尾
        public int end;
        /*代表从这个结点经过某一个字符可以到达的结点,也就是从这个结点开始的边的个数,
        加入这个前缀树存储的都是小写字母的话,将素组大小设为26,这样,字母a-z,就对应
        数组中索引为0-25。如果字符串的组成不是小写字母,比如是中文字符串,可以使用一个
        hashMap<Integer,TrieNode>key是字母的ASCII码,value,是从这个结点开始,通过
        key能到达的结点。
         */
        public TrieNode[] nexts;

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

    //树
    public static class Trie{
        //定义一个根结点
        private TrieNode root = new TrieNode();

        //向前缀树中插入字符串
        public void insert(String word){
            if(word==null||word.length()==0) return;
            //先把word变成字符类型的数组
            char[] chars = word.toCharArray();
            TrieNode node = root;
            //index为字母对应的数组索引
            int index = 0;
            for(int i = 0; i < chars.length; i++){
                index = chars[i] - 'a';
                //判断从root结点,能不能通过字母走向下一个结点
                if(node.nexts[index] == null){
                    //如果从root节点,不能通过字母走向下一个结点
                    node.nexts[index] = new TrieNode();
                }
                //如果从root结点,可以通过字母到下一个结点
                node = node.nexts[index];
                node.pass++;
            }
            //最后一个结点的end++,表示以这个结点结尾的字符串数
            node.end++;
        }

        //查询一个字符串出现的次数
        public int search(String word){
            if(word == null || word.length() == 0) return 0;
            //先将字符串变成字符数组
            char[] chars = word.toCharArray();
            TrieNode node = root;
            int index = 0;
            for(int i = 0; i < chars.length; i++){
                index = chars[i] - 'a';
                //只要结点的nexts数组的index下没有存结点,就说明前缀树中没有存储这个字符串
                if(node.nexts[index] == null){
                    return 0;
                }
                node = node.nexts[index];
            }
            //返回最后一个结点的end值
            return node.end;
        }

        //前缀树删除一个字符串
        public void delete(String word){
            //首先查询寻前缀树中是否有字符串
            if(search(word) > 0){
                //将字符串转换成字符数组
                char[] chars = word.toCharArray();
                TrieNode node = root;
                int index = 0;
                for(int i = 0; i < chars.length; i++){
                    /*判断当前结点的下一个结点的path值是否为1,为1,说明只有一个字符串
                    经过下一个结点,直接将当前结点的index位置设为null,如果不为1,说明
                    还有其他字符串经过下一个结点,也就是说,字符串有相同的开头,那么就让
                    下一个结点的path--,继续往下寻找,知道找到下一个结点的path为1的结点,
                    将结点的nexts的index位置设为null
                     */
                    index = chars[i] - 'a';
                    if(node.nexts[index].pass-- == 1 ){
                        //说明只有一个字符串经过这个结点,直接将数组对应的索引位置设为null
                        node.nexts[index] = null;
                        return;
                    }
                    //如果有两个或两个以上的字符串经过结点,那么这个结点
                    node = node.nexts[index];
                    //当添加多个相同的字符串,并且删除其中一个的时候,会执行--操作
                    node.end--;
                }
            }
        }

        //查找指定前缀的字符串个数
        public int samePrefixNum(String word){
            if(word == null || word.length() == 0) return 0;
            char[] chars = word.toCharArray();
            int index = 0;
            TrieNode node = root;
            for(int i = 0; i < chars.length; i++){
                index = chars[i] - 'a';
                //中间有一个找不到,就返回0
                if(node.nexts[index] == null){
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.pass;
        }
    }

    public static void main(String[] args) {
        Trie trieTree = new Trie();
        System.out.println(trieTree.search("ewen"));//0
        trieTree.insert("ewen");
        System.out.println(trieTree.search("ewen"));//1
        trieTree.insert("ewen");
        trieTree.insert("ewen");
        trieTree.insert("ewen");
        System.out.println(trieTree.search("ewen"));//4
        trieTree.delete("ewen");
        System.out.println(trieTree.search("ewen"));//3
        System.out.println(trieTree.samePrefixNum("ew"));//3
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的C语言实现前缀树的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LEN 32 struct trie_node { int is_end; struct trie_node* children[2]; }; struct trie_node* create_node() { struct trie_node* node = (struct trie_node*)malloc(sizeof(struct trie_node)); node->is_end = 0; node->children[0] = node->children[1] = NULL; return node; } void insert(struct trie_node* root, char* ip) { struct trie_node* cur = root; int len = strlen(ip); for (int i = 0; i < len; i++) { int idx = ip[i] - '0'; if (cur->children[idx] == NULL) { cur->children[idx] = create_node(); } cur = cur->children[idx]; } cur->is_end = 1; } int search(struct trie_node* root, char* ip) { struct trie_node* cur = root; int len = strlen(ip); for (int i = 0; i < len; i++) { int idx = ip[i] - '0'; if (cur->children[idx] == NULL) { return 0; } cur = cur->children[idx]; } return cur->is_end; } int main() { char ip[MAX_LEN]; struct trie_node* root = create_node(); insert(root, "10101101"); insert(root, "10101110"); insert(root, "11000011"); insert(root, "11000100"); while (1) { printf("Enter an IP address: "); scanf("%s", ip); if (strcmp(ip, "exit") == 0) { break; } if (search(root, ip)) { printf("IP found\n"); } else { printf("IP not found\n"); } } return 0; } ``` 该代码实现了一个简单的前缀树,可以用于IP地址的插入和查找。在代码中,使用结构体`trie_node`表示前缀树的节点,其中`is_end`表示该节点是否为一个IP地址的结束节点,`children`数组表示该节点的子节点。函数`create_node`用于创建一个新的节点,函数`insert`用于将一个IP地址插入到前缀树中,函数`search`用于查找一个IP地址是否存在于前缀树中。在`main`函数中,程序可以不断输入IP地址进行查找,直到输入“exit”退出程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值