package com.jwetherell.algorithms.data_structures; import com.jwetherell.algorithms.data_structures.interfaces.IMap; /** * A radix trie or radix tree is a space-optimized trie data structure where * each node with only one child is merged with its child. The result is that * every internal node has at least two children. Unlike in regular tries, edges * can be labeled with sequences of characters as well as single characters. * This makes them much more efficient for small sets (especially if the strings * are long) and for sets of strings that share long prefixes. This particular * radix tree is used to represent an associative array. * * This implementation is a composition of a PatriciaTrie as the backing * structure. * * http://en.wikipedia.org/wiki/Radix_tree * http://en.wikipedia.org/wiki/Associative_array * * @author Justin Wetherell */ @SuppressWarnings("unchecked") public class RadixTrie implements PatriciaTrie.INodeCreator, IMap { private PatriciaTrie trie = null; public RadixTrie() { trie = new PatriciaTrie(this); } /** * {@inheritDoc} */ @Override public V put(K key, V value) { V prev = null; PatriciaTrie.Node node = trie.addSequence(key); if (node!=null && node instanceof RadixNode) { RadixNode radixNode = (RadixNode) node; if (radixNode.value!=null) prev = radixNode.value; radixNode.value = value; } return prev; } /** * {@inheritDoc} */ @Override public V get(K key) { PatriciaTrie.Node node = trie.getNode(key); if (node!=null && node instanceof RadixNode) { RadixNode radixNode = (RadixNode) node; return radixNode.value; } return null; } /** * {@inheritDoc} */ @Override public boolean contains(K key) { return trie.contains(key); } /** * {@inheritDoc} */ @Override public V remove(K key) { PatriciaTrie.Node node = trie.getNode(key); V value = null; if (node!=null) { if (node instanceof RadixNode) { RadixNode rn = (RadixNode)node; value = rn.value; rn.value = null; } trie.remove(node); } return value; } /** * {@inheritDoc} */ @Override public void clear() { trie.clear(); } /** * {@inheritDoc} */ @Override public int size() { return trie.size(); } /** * {@inheritDoc} */ @Override public boolean validate() { java.util.Set keys = new java.util.HashSet(); PatriciaTrie.Node node = trie.root; if (node!=null && !validate(node,"",keys)) return false; return (keys.size()==size()); } private boolean validate(PatriciaTrie.Node node, String string, java.util.Set keys) { if (!(node instanceof RadixNode)) return false; RadixNode tmn = (RadixNode)node; StringBuilder builder = new StringBuilder(string); if (tmn.string!=null) builder.append(tmn.string); String s = builder.toString(); if (tmn.type == PatriciaTrie.WHITE) { K k = (K)s; V v = tmn.value; if (k==null || v==null) return false; if (keys.contains(k)) return false; keys.add(k); } for (int i=0; i toMap() { return (new JavaCompatibleRadixTrieMap(this)); } /** * {@inheritDoc} */ @Override public String toString() { return RadixTreePrinter.getString(this); } /** * {@inheritDoc} */ @Override public PatriciaTrie.Node createNewNode(PatriciaTrie.Node parent, char[] seq, boolean type) { return (new RadixNode(parent, seq, type)); } protected static final class RadixNode extends PatriciaTrie.Node { protected V value = null; protected RadixNode(PatriciaTrie.Node node, V value) { super(node.parent, node.string, node.type); this.value = value; for (int i = 0; i < node.getChildrenSize(); i++) { PatriciaTrie.Node c = node.getChild(i); this.addChild(c); } } protected RadixNode(PatriciaTrie.Node parent, char[] string, boolean type) { super(parent, string, type); } protected RadixNode(PatriciaTrie.Node parent, char[] string, boolean type, V value) { super(parent, string, type); this.value = value; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()); builder.append("value = ").append(value).append("\n"); return builder.toString(); } } protected static class RadixTreePrinter { public static String getString(RadixTrie map) { return getString(map.trie.root, "", null, true); } protected static String getString(PatriciaTrie.Node node, String prefix, String previousString, boolean isTail) { StringBuilder builder = new StringBuilder(); String string = null; if (node.string != null) { String temp = String.valueOf(node.string); if (previousString != null) string = previousString + temp; else string = temp; } if (node instanceof RadixNode) { RadixNode radix = (RadixNode) node; builder.append(prefix + (isTail ? "└── " : "├── ") + ((radix.string != null) ? "(" + String.valueOf(radix.string) + ") " + "[" + ((node.type == PatriciaTrie.WHITE) ? "WHITE" : "BLACK") + "] " + string + ((radix.value != null) ? " = " + radix.value : "") : "[" + ((node.type == PatriciaTrie.WHITE) ? "WHITE" : "BLACK") + "]") + "\n"); } if (node.getChildrenSize() > 0) { for (int i = 0; i < node.getChildrenSize() - 1; i++) { builder.append(getString(node.getChild(i), prefix + (isTail ? " " : "│ "), string, false)); } if (node.getChildrenSize() >= 1) { builder.append(getString(node.getChild(node.getChildrenSize() - 1), prefix + (isTail ? " " : "│ "), string, true)); } } return builder.toString(); } } private static class JavaCompatibleIteratorWrapper implements java.util.Iterator> { private RadixTrie map = null; private java.util.Iterator> iter = null; private java.util.Map.Entry lastEntry = null; public JavaCompatibleIteratorWrapper(RadixTrie map, java.util.Iterator> iter) { this.map = map; this.iter = iter; } /** * {@inheritDoc} */ @Override public boolean hasNext() { if (iter==null) return false; return iter.hasNext(); } /** * {@inheritDoc} */ @Override public java.util.Map.Entry next() { if (iter==null) return null; lastEntry = iter.next(); return lastEntry; } /** * {@inheritDoc} */ @Override public void remove() { if (iter==null || lastEntry==null) return; map.remove(lastEntry.getKey()); iter.remove(); } } private static class JavaCompatibleMapEntry extends java.util.AbstractMap.SimpleEntry { private static final long serialVersionUID = -4427602384853830561L; public JavaCompatibleMapEntry(K key, V value) { super(key, value); } } private static class JavaCompatibleRadixTrieMap extends java.util.AbstractMap { private RadixTrie map = null; protected JavaCompatibleRadixTrieMap(RadixTrie map) { this.map = map; } /** * {@inheritDoc} */ @Override public V put(K key, V value) { return map.put(key, value); } /** * {@inheritDoc} */ @Override public V remove(Object key) { return map.remove((K)key); } /** * {@inheritDoc} */ @Override public void clear() { map.clear(); } /** * {@inheritDoc} */ @Override public boolean containsKey(Object key) { return map.contains((K)key); } /** * {@inheritDoc} */ @Override public int size() { return map.size(); } /** * {@inheritDoc} */ @Override public java.util.Set> entrySet() { java.util.Set> set = new java.util.HashSet>() { private static final long serialVersionUID = 1L; /** * {@inheritDoc} */ @Override public java.util.Iterator> iterator() { return (new JavaCompatibleIteratorWrapper(map,super.iterator())); } }; if (map.trie!=null && map.trie.root!=null) { PatriciaTrie.Node n = map.trie.root; levelOrder(n,"",set); } return set; } private void levelOrder(PatriciaTrie.Node node, String string, java.util.Set> set) { StringBuilder builder = new StringBuilder(string); RadixNode tmn = null; if (node instanceof RadixNode) { tmn = (RadixNode)node; if (tmn.string!=null) builder.append(tmn.string); if (tmn.type == PatriciaTrie.WHITE) { K s = (K)builder.toString(); java.util.Map.Entry entry = new JavaCompatibleMapEntry(s, tmn.value); set.add(entry); } } String b = builder.toString(); for (int i=0; i