算法学习06: 前缀树

前缀树(Trie树)

前缀树(Trie树)每个节点存储一个字符,从根节点向下对应一个字符串的前缀.

前缀树的用途

  1. 查询以某字符串开头的字符串: 可以实现,直接从根节点向下撸
  2. 查询是否存在"be"这个字符串? 原始的前缀树不能判断,“bef"会覆盖"be”,
    因此给节点加一个数据项,表示有多少字符串以此字符结尾,这样就可以查加入了几次"be"
  3. 再扩充一个功能? 给你一个字符串,问有多少字符串以它为前缀?
    再加一个数据项: 表示每一个节点被滑过多少次.

前缀树的特点

加入查询的操作与数据量无关,只与被操作字符串长度有关.

前缀树的实现leetcode 208

前缀树节点结构: 前缀树的节点为26字母之一,因此不将其字母写在节点上,而是将子节点写进一个长度为26的数组,若数组对应index为空则说明无节点,否则存在该节点.

class TrieNode {
	public int path;
	public int end;
	public TrieNode[] nexts;

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

在实现前缀树时,我们并没有将字母值放在节点上,而是将其放在边上(数组或map),这样方便遍历的同时也节约了一个成员变量

public class TrieTree {
	private TrieNode root;	// 存储字典树根

	public TrieTree() {
		root = new TrieNode();
	}
	
	// 向前缀树中添加一个字符串
	public void insert(String word);

	// 在前缀树中查询一个字符串的出现次数
	public int search(String word);

	// 从前缀树中删除一个字符串
	public void delete(String word);

	// 查找以某字符串开头的长字符串数
	public int prefixNumber(String pre);
}

下面是具体方法的实现:

  1. insert(String word)方法: 沿着要加入的字符串一路查找下去,若子节点存在就滑过去,若不存在就建立对应索引的节点. 中途经过的节点的path++,遇到末尾节点,则end++.

    // 向前缀树中添加一个字符串
    public void insert(String word) {
    	if (word == null) {
    		return;
    	}
    	// 把字符串转化为字符数组
    	char[] chs = word.toCharArray();
    	TrieNode node = root;
    	// 从根节点沿着字符数组向下滑
    	for (char ch : chs) {
    		int index = ch - 'a';
    		// 若对应索引的节点不存在,则创建该节点
    		if (node.nexts[index] == null) {
    			node.nexts[index] = new TrieNode();
    		}
    		// 若对应索引的节点存在,则滑过该节点
    		// 先滑到下一个节点,然后再path++,下边两句话不能反了
    		node = node.nexts[index];
    		node.path++;
    	}
    	node.end++;
    }
    
  2. search(String word): 查询字符串出现次数,遍历方式与添加相同. 中途若有节点找不到则返回0,否则找到末尾节点并返回其end成员变量.

    // 在前缀树中查询一个字符串的出现次数
    public int search(String word) {
    	if (word == null) {
    		return 0;
    	}
    	char[] chs = word.toCharArray();
    	TrieNode node = root;
    	// 查询与添加一样,沿着字符数组一路滑下去
    	for (char ch : chs) {
    		int index = ch - 'a';
    		if (node.nexts[index] == null) {
    		// 若某中途节点不存在,则后边节点一定不存在,字符串出现次数为0
    			return 0;
    		}
    		node = node.nexts[index];
    	}
    	// 返回以末尾节点结束的字符串的个数
    	return node.end;
    }
    
  3. delete(String word)方法: 先查询该字符串是否存在,若不存在,则不用删.若字符串存在,则将沿途节点出现次数减一,若某节点被删除,其子节点必然被删除,因此可以直接返回,让jvm回收其子节点.

    // 从前缀树中删除一个字符串
    public void delete(String word) {
    	// 先查询字符串是否存在,若不存在则不用删除了
    	if (search(word) != 0) {
    		char[] chs = word.toCharArray();
    		TrieNode node = root;
    		// 遍历节点,将沿途节点出现次数减一
    		for (char ch : chs) {
    			int index = ch - 'a';
    			// 若中途某节点出现次数被减到0次,则其后节点也将不存在,直接返回即可
    			if (--node.nexts[index].path == 0) {
    				node.nexts[index] = null;
    				return;
    			}
    			node = node.nexts[index];
    		}
    		node.end--;
    	}
    }
    
  4. prefixNumber(String pre)方法:找到前缀的最后一个节点,返回其path成员变量值

    // 查找以某字符串开头的长字符串数
    public int prefixNumber(String pre) {
    	if (pre == null) {
    		return 0;
    	}
    	char[] chs = pre.toCharArray();
    	TrieNode node = root;
    	// 遍历找到前缀的最后一个节点
    	for (char ch : chs) {
    		int index = ch - 'a';
    		if (node.nexts[index] == null) {
    			return 0;
    		}
    		node = node.nexts[index];
    	}
    	// 返回前缀最后一个节点的path变量
    	return node.path;
    }
    

leetcode上的前缀树习题: leetcode 745,leetcode 14,字典树专题

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值