【数据结构】Trie Tree:字典树(前缀树)的实现

字典树又称为前缀树或Trie树,是处理字符串常见的数据结构。假设组成所有单词的字符仅为a-z。

字典树介绍

字典树是一种树形结构,优点是利用字符串的公共前缀来节约存储空间,比如加入"abc"、“abcd”、“abd”、“b”、“bcd”、“efg”、"hik"之后,字典树如图所示。
【数据结构】Trie Tree:字典树(前缀树)的实现_第1张图片

基本特性

  • 根节点没有字符路径。除了根节点外,每一个节点都被一个字符路径找到。
  • 从根节点到某一节点,将路径上经过的字符连接起来,为扫过的对应字符串。
  • 每个节点向下所有的字符路径上的字符都不同。

在字典树上搜索添加过的单词步骤为:

  1. 从根节点开始搜索。
  2. 取得要查找单词的第一个字母,并根据该字母选择对应的字符路径向下继续搜索。
  3. 字符路径指向的第二层节点上,根据第二个字母选择对应的字符路径向下继续搜索。
  4. 一直向下搜索,如果单词搜索完后,找到的最后一个节点是一个终止节点,比如图中的实心节点,说明字典树中含有这个单词,如果找到的最后一个节点不是一个终止节点,说明单词不是字典树中添加过的单词。如果还没搜索完,但是已经没有后续节点了,也说明单词不是字典树中添加过的单词。

数据结构类型定义

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

	public TrieNode() {
		path = 0;
		end = 0;
		map = new TrieNode[26];
	}
}
Variable 用途
path 表示有多少个单词共用这个节点
end 表示有多少个单词以这个节点结尾
map 实质为一个哈希表结构,key代表该节点的一条字符路径,value表示字符路径指向的节点

注:在字符类较多的情况下,可以选择真实的哈希表结构实现map。

Trie树实现

  • void insert(String word):假设word的长度为N,从左到右遍历word中的每一个字符,并以此从头节点开始根据每一个word[i],找到下一个节点。
    1)如果找的过程中节点不存在,就建立新节点,记为a,并令a.path=1。
    2)如果节点存在,记为b,令b.path++。
    3)通过最后一个字符(word[N-1])找到最后一个节点时记为e,令e.path++,e.paht++。
  • boolean search(String word):从左到右遍历word中每一个字符,并以此从头节点开始根据每一个word[i],找到下一个节点。
    1)如果找的过程中节点不存在,说明这个单词的整个部分没有添加到Trie树中,否则不可能找的过程中节点不存在,直接返回false。
    2)如果通过word[N-1]找到最后一个节点,记为e,如果e.end!=0,说明有单词通过word[N-1]的字符路径,并以节点e结尾,返回true;如果e.end==0,返回false;
  • void delete(String word):先调用search(word),看word是否在Trie树中;
    1)若不在,直接返回;
    2)如在,从左到右遍历word中的每个字符,并依次从头节点开始,根据每一个word[i]找到下一个的节点。在找的过程中,把扫过的每一个节点的path值减1。如果发现下一个节点的path值减完之后已经为0,直接从当前节点的map中删除后续的所有路径,返回即可。如果扫到最后一个节点,记为e,令e.path–,e.end–。
  • int prefixNumber(String pre):和search操作同理,根据pre不断找到节点,假设最后的节点记为e,返回e.path的值即可。

代码实现

/**   
* @Title: Trie.java
* @Package com.hf.domain
* @Description: TODO
* @author hf寒沨  
* @date 2019年1月19日 下午2:51:50
* @version V1.0   
*/
package com.hf.domain;

/**
 * @ClassName: Trie
 * @Description:
 * @author hf寒沨
 * @date 2019年1月19日 下午2:51:50
 * 
 */
public class Trie {
	private TrieNode root;

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

	/**
	 * 
	* @Title: insert
	* @Description: insert word
	* @param @param word    
	* @return void   
	* @throws
	 */
	public void insert(String word) {
		if (word == null) {
			return;
		}
		char[] chs = word.toCharArray();
		TrieNode node = root;
		int index = 0;
		for (int i = 0; i < chs.length; i++) {
			index = chs[i] - 'a';
			if (node.map[index] == null) {
				node.map[index] = new TrieNode();
			}
			node = node.map[index];
			node.path++;
		}
		node.end++;
	}

	/**
	 * 
	* @Title: search
	* @Description: search word
	* @param @param word
	* @param @return    
	* @return boolean
	* @throws
	 */
	public boolean search(String word) {
		if (word == null) {
			return false;
		}
		char[] chs = word.toCharArray();
		TrieNode node = root;
		int index = 0;
		for (int i = 0; i < chs.length; i++) {
			index = chs[i] - 'a';
			if (node.map[index] == null) {
				return false;
			}
			node = node.map[index];
		}
		return node.end != 0;
	}

	/**
	 * 
	 * @Title: delete
	 * @Description: delete word from Trie Tree
	 * @param @param word   
	 * @return void    
	 * @throws
	 */
	public void delete(String word) {
		if (search(word)) {
			char[] chs = word.toCharArray();
			TrieNode node = root;
			int index = 0;
			for (int i = 0; i < chs.length; i++) {
				index = chs[i] - 'a';
				if (node.map[index].path-- == 1) {
					node.map[index] = null;
					return;
				}
				node = node.map[index];
			}
			node.end--;
		}
	}

	/**
	 * 
	* @Title: prefixNumber
	* @Description: count the number of word with the common prefix
	* @param @param pre
	* @param @return     
	* @return int     
	* @throws
	 */
	public int prefixNumber(String pre) {
		if (pre == null) {
			return 0;
		}
		char[] chs = pre.toCharArray();
		TrieNode node = root;
		int index = 0;
		for (int i = 0; i < chs.length; i++) {
			index = chs[i] - 'a';
			if (node.map[index] == null) {
				return 0;
			}
			node = node.map[index];
		}
		return node.path;
	}

}

参考文献
[1] 左程云:程序员代码面试指南

你可能感兴趣的:(数据结构,算法,Java,LeetCode)