[数据结构] Trie树

引入

字典树是一种假想数据结构(数据结构不都是假想的吗 )

字典树又名前缀树,Trie树,是一种存储大量字符串的树形数据结构,相比于HashMap存储,在存储单词(和语种无关,任意语言都可以)的场景上,节省了大量的内存空间。

对于字典树,作为树,自然有元素之间的继承与一对多关系,每一个元素都可以有几个子元素,作为它之后的字母;
倘若要比对两个字符串是否相同,只需要比对在这棵字典树上,这两个串最后一个元素的祖先链(即前缀)是否相同,并且对于祖先链来说,并不用逐个比较,只需要记录访问就行
前缀树有什么应用吗,字符串的前缀操作处理很重要,例如百度的搜索等等等等在这里插入图片描述

基本操作

初始化

class Trie {

    TrieNode root;

    public Trie() {
        root = new TrieNode();
    }
    
    class TrieNode {
        char val;
        boolean isEnd = false;
        TrieNode[] child = new TrieNode[26];
        //数组大小跟字符类型的数量相关,我这里只考虑26小写字母

        public TrieNode() {
        }

        public TrieNode(char val) {
            this.val = val;
        }
    }
}

插入

    //插入单词操作
    public void insert(String word) {
        TrieNode p = root;      //创建移动节点
        for (int i = 0; i < word.length(); ++i) {
            char ch = word.charAt(i);
            if (p.child[ch - 'a'] == null) {  //判断该结点是否有过前缀
                p.child[ch - 'a'] = new TrieNode(ch);         //没有则创建
            }
            //移动到下一个节点
            p = p.child[ch - 'a'];
            //打上末尾字符标记
            if (i == word.length() - 1) {
                p.isEnd = true;
            }
        }
    }

每个节点的所有的边都不同,这条性质可以便于我们判断在某一棵字典树到底有没有某条链:只要前缀不符合,就不需要再判断,因为必然没有(同一深度、同一父亲,边与边必定互异

查询

查询操作,只需要一边判断是否符合要求,一边判断是否继续迭代即可。
如果在遍历过程中发现字母有缺失,即下一个字母为null,则返回false, 表示前缀树种不存在这个单词。
如果遍历完成,就要判断此时p是否为单词末尾:如果是,返回true;如果不是,说明word仅仅是一个前缀,并不完整,返回false

    //查询操作
    public boolean search(String word) {
        TrieNode p = root;
        for (int i = 0; i < word.length(); ++i) {
            int c = word.charAt(i) - 'a';
            if (p.child[c] == null) {   //一旦发现某个字母未出现在前缀上,返回false
                return false;
            }
            p = p.child[c];
        }
        return p.isEnd;
    }

若要判断有无前缀,与此代码思路相同,返回值改为true即可

    //有无前缀
    public boolean search(String word) {
        TrieNode p = root;
        for (int i = 0; i < word.length(); ++i) {
            int c = word.charAt(i) - 'a';
            if (p.child[c] == null) {   //一旦发现某个字母未出现在前缀上,返回false
                return false;
            }
            p = p.child[c];
        }
        return true;
    }

数组实现方式

在此附上数组实现方式(C)
基本思想不变,只是以数组实现,故在此不再赘述

#include <bits/stdc++.h>
using namespace std;
int trie[100010][26];
int idx, cnt[100010];

void insert1(char s[]) {
	int p = 0;
	for(int i = 0; i < strlen(s); i++) {
		int u = s[i] - 'a';
		if(trie[p][u] == 0) {
			trie[p][u] = ++idx;
		}
		p = trie[p][u];
	}
	cnt[p]++;
}

int query(char s[]) {
	int p = 0;
	for(int i = 0; i < strlen(s); i++) {
		int u = s[i] - 'a';
		if(!trie[p][u]) {
			return 0;
		}
		p = trie[p][u];
	}
	return cnt[p];
}

例题

【leetcode】208.实现Trie(前缀树)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xoliu1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值