1. 程式人生 > >Trie(字典樹)的Java實現

Trie(字典樹)的Java實現

簡單實現了一個具有CRUD操作能力的Trie。CRUD操作即插入(Create),讀取(Read),改變(Update)和刪除(Delete)。

刪除是基於當前結點的count實現的。當count為0時代表當前結點應該被完全刪除。

Trie的所有操作均為O(n)。其中n為所要操作的單詞的長度。

public KeyNotFound extends Exception {
    public KeyNotFound(String message) {
        super(message);
    }
}

public class Trie {
    class TrieNode {
        char
curr; TrieNode[] children; private boolean isEnd; String data; private int count; TrieNode (char curr, boolean isEnd, String data) { // Cast all the chars to lower case curr = Character.toLowerCase(curr); this.curr = curr; this
.isEnd = isEnd; this.data = data; this.children = new TrieNode[26]; this.count = 1; } void setEnd(boolean end) { this.isEnd = end; } void increaseCount() { this.count++; } int getCount() { return
this.count; } void decreaseCount() { this.count--; } } private TrieNode root; public Trie() { /** Initialize the Trie. */ this.root = new TrieNode(' ', false, ""); } public void insert(String word, String data) { /** Inserts a word and corresponding data into the trie. */ char[] chars = word.toCharArray(); TrieNode currPos = this.root; for (int i = 0; i < chars.length; i++) { int index = chars[i] - 'a'; // If a node corresponding to current substring does not exists, create one. if (currPos.children[index] == null) { TrieNode newNode = new TrieNode(chars[i], false, ""); currPos.children[index] = newNode; } // If this is the end of the word, store the data in to the Trie and set end. if (i == chars.length - 1) { currPos.children[index].setEnd(true); currPos.children[index].data = data; } // Move one level down currPos = currPos.children[index]; } } public boolean search(String word) { /** Returns if the word is in the Trie. */ char[] chars = word.toCharArray(); TrieNode currPos = this.root; for (int i = 0; i < chars.length; i++) { int index = chars[i] - 'a'; // If no match find, return false if (currPos.children[index] == null) { return false; } if (i == chars.length - 1 && currPos.children[index].isEnd) { return true; } currPos = currPos.children[index]; } return false; } public boolean startsWith(String prefix) { /** Returns if there is any word in the Trie that starts with the given prefix. */ char[] chars = prefix.toCharArray(); TrieNode currPos = this.root; for (int i = 0; i < chars.length; i++) { int index = chars[i] - 'a'; if (currPos.children[index] == null) { return false; } currPos = currPos.children[index]; } return true; } public String getData(String key) throws KeyNotFound { /** * Gets the corresponding data for the given key if the key exists in the Trie. * Throw KeyNotFound error if no such key exists */ char[] chars = key.toCharArray(); TrieNode currPos = this.root; for (int i = 0; i < chars.length; i++) { int index = chars[i] - 'a'; // If no match find, return empty string if (currPos.children[index] == null) { throw new KeyNotFound("Given key does not exists in the Trie!"); } currPos = currPos.children[index]; } if (!currPos.isEnd) { throw new KeyNotFound("Given key does not exists in the Trie!"); } return currPos.data; } public boolean setData(String key, String newData) { /** * Change the data of an existing key. * If changed successfully, return true. * If the key doesn't exists, return false. */ char[] chars = key.toCharArray(); TrieNode currPos = this.root; for (int i = 0; i < chars.length; i++) { int index = chars[i] - 'a'; // If no match find, cannot store the data, return false if (currPos.children[index] == null) { return false; } currPos = currPos.children[index]; } if (!currPos.isEnd) { return false; } currPos.data = newData; return true; } public void delete(String word) { /** * Delete the given word in the Trie if exists */ // If the word doesn't exist in the Trie, should delete anything if (!search(word)) { return; } char[] chars = word.toCharArray(); TrieNode currPos = this.root; for (int i = 0; i < chars.length; i++) { int index = chars[i] - 'a'; TrieNode tmp = currPos.children[index]; if (currPos.children[index].getCount() == 1) { currPos.children[index] = null; } else { currPos.children[index].decreaseCount(); } // if a word is deleted but the prefix still exists, delete the data and flip isEnd flag. if (i == chars.length - 1) { if (currPos.children[index] != null) { currPos.children[index].data = ""; currPos.children[index].setEnd(false); } } currPos = tmp; } } }