349 lines
9.6 KiB
Java
349 lines
9.6 KiB
Java
|
package com.jwetherell.algorithms.data_structures;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.List;
|
||
|
|
||
|
import com.jwetherell.algorithms.data_structures.BinarySearchTree.INodeCreator;
|
||
|
import com.jwetherell.algorithms.data_structures.BinarySearchTree.Node;
|
||
|
import com.jwetherell.algorithms.data_structures.interfaces.IMap;
|
||
|
|
||
|
/**
|
||
|
* An tree used to store key->values pairs, this is an implementation of an
|
||
|
* associative array.
|
||
|
*
|
||
|
* This implementation is a composition of a AVLTree as the backing structure.
|
||
|
*
|
||
|
* http://en.wikipedia.org/wiki/AVL_tree
|
||
|
* http://en.wikipedia.org/wiki/Associative_array
|
||
|
*
|
||
|
* @author Justin Wetherell <phishman3579@gmail.com>
|
||
|
*/
|
||
|
@SuppressWarnings("unchecked")
|
||
|
public class TreeMap<K extends Comparable<K>, V> implements IMap<K,V> {
|
||
|
|
||
|
private final BinarySearchTree.INodeCreator<K> creator = new BinarySearchTree.INodeCreator<K>() {
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public BinarySearchTree.Node<K> createNewNode(BinarySearchTree.Node<K> parent, K id) {
|
||
|
return (new TreeMapNode<K, V>(parent, id, null));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
private AVLTree<K> tree = null;
|
||
|
|
||
|
public TreeMap() {
|
||
|
|
||
|
tree = new AVLTree<K>(creator);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructor with external Node creator.
|
||
|
*/
|
||
|
public TreeMap(INodeCreator<K> creator) {
|
||
|
tree = new AVLTree<K>(creator);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public V put(K key, V value) {
|
||
|
V prev = null;
|
||
|
BinarySearchTree.Node<K> node = tree.addValue(key);
|
||
|
|
||
|
TreeMapNode<K, V> treeMapNode = (TreeMapNode<K, V>) node;
|
||
|
if (treeMapNode.value!=null)
|
||
|
prev = treeMapNode.value;
|
||
|
treeMapNode.value = value;
|
||
|
|
||
|
return prev;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public V get(K key) {
|
||
|
BinarySearchTree.Node<K> node = tree.getNode(key);
|
||
|
TreeMapNode<K, V> mapNode = (TreeMapNode<K, V>) node;
|
||
|
return mapNode.value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public boolean contains(K key) {
|
||
|
return tree.contains(key);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public V remove(K key) {
|
||
|
Node<K> node = tree.removeValue(key);
|
||
|
TreeMapNode<K, V> treeMapNode = (TreeMapNode<K, V>) node;
|
||
|
V value = null;
|
||
|
if (treeMapNode!=null) {
|
||
|
value = treeMapNode.value;
|
||
|
treeMapNode.id = null;
|
||
|
treeMapNode.value = null;
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public void clear() {
|
||
|
tree.clear();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public int size() {
|
||
|
return tree.size();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public boolean validate() {
|
||
|
java.util.Set<K> keys = new java.util.HashSet<K>();
|
||
|
Node<K> node = tree.root;
|
||
|
if (node!=null && !validate(node,keys))
|
||
|
return false;
|
||
|
return (keys.size()==size());
|
||
|
}
|
||
|
|
||
|
private boolean validate(Node<K> node, java.util.Set<K> keys) {
|
||
|
if (!(node instanceof TreeMapNode))
|
||
|
return false;
|
||
|
|
||
|
TreeMapNode<K,V> tmn = (TreeMapNode<K,V>)node;
|
||
|
K k = tmn.id;
|
||
|
V v = tmn.value;
|
||
|
if (k==null || v==null)
|
||
|
return false;
|
||
|
if (keys.contains(k))
|
||
|
return false;
|
||
|
keys.add(k);
|
||
|
if (tmn.lesser!=null && !validate(tmn.lesser,keys))
|
||
|
return false;
|
||
|
if (tmn.greater!=null && !validate(tmn.greater,keys))
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public java.util.Map<K,V> toMap() {
|
||
|
return (new JavaCompatibleTreeMap<K,V>(this));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return TreeMapPrinter.getString(this);
|
||
|
}
|
||
|
|
||
|
protected static class TreeMapNode<K extends Comparable<K>, V> extends AVLTree.AVLNode<K> {
|
||
|
|
||
|
protected V value = null;
|
||
|
|
||
|
protected TreeMapNode(RedBlackTree.Node<K> parent, K key, V value) {
|
||
|
super(parent, key);
|
||
|
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 TreeMapPrinter {
|
||
|
|
||
|
public static <K extends Comparable<K>, V> String getString(TreeMap<K, V> map) {
|
||
|
if (map.tree.root == null) return "Tree has no nodes.";
|
||
|
return getString((TreeMapNode<K, V>) map.tree.root, "", true);
|
||
|
}
|
||
|
|
||
|
private static <K extends Comparable<K>, V> String getString(TreeMapNode<K, V> node, String prefix, boolean isTail) {
|
||
|
StringBuilder builder = new StringBuilder();
|
||
|
|
||
|
builder.append(prefix + (isTail ? "└── " : "├── ") + ((node.id != null) ? (node.id + " = " + node.value) : node.id) + "\n");
|
||
|
List<TreeMapNode<K, V>> children = null;
|
||
|
if (node.lesser != null || node.greater != null) {
|
||
|
children = new ArrayList<TreeMapNode<K, V>>(2);
|
||
|
if (node.lesser != null) children.add((TreeMapNode<K, V>) node.lesser);
|
||
|
if (node.greater != null) children.add((TreeMapNode<K, V>) node.greater);
|
||
|
}
|
||
|
if (children != null) {
|
||
|
for (int i = 0; i < children.size() - 1; i++) {
|
||
|
builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false));
|
||
|
}
|
||
|
if (children.size() >= 1) {
|
||
|
builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return builder.toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static class JavaCompatibleIteratorWrapper<K extends Comparable<K>,V> implements java.util.Iterator<java.util.Map.Entry<K, V>> {
|
||
|
|
||
|
private TreeMap<K,V> map = null;
|
||
|
private java.util.Iterator<java.util.Map.Entry<K, V>> iter = null;
|
||
|
private java.util.Map.Entry<K, V> lastEntry = null;
|
||
|
|
||
|
public JavaCompatibleIteratorWrapper(TreeMap<K,V> map, java.util.Iterator<java.util.Map.Entry<K, V>> 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<K, V> 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<K extends Comparable<K>,V> extends java.util.AbstractMap.SimpleEntry<K,V> {
|
||
|
|
||
|
private static final long serialVersionUID = 3282082818647198608L;
|
||
|
|
||
|
public JavaCompatibleMapEntry(K key, V value) {
|
||
|
super(key, value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static class JavaCompatibleTreeMap<K extends Comparable<K>,V> extends java.util.AbstractMap<K,V> {
|
||
|
|
||
|
private TreeMap<K,V> map = null;
|
||
|
|
||
|
protected JavaCompatibleTreeMap(TreeMap<K,V> 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<java.util.Map.Entry<K, V>> entrySet() {
|
||
|
java.util.Set<java.util.Map.Entry<K, V>> set = new java.util.HashSet<java.util.Map.Entry<K, V>>() {
|
||
|
|
||
|
private static final long serialVersionUID = 1L;
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public java.util.Iterator<java.util.Map.Entry<K, V>> iterator() {
|
||
|
return (new JavaCompatibleIteratorWrapper<K,V>(map,super.iterator()));
|
||
|
}
|
||
|
};
|
||
|
if (map.tree!=null && map.tree.root!=null) {
|
||
|
Node<K> n = map.tree.root;
|
||
|
levelOrder(n,set);
|
||
|
}
|
||
|
return set;
|
||
|
}
|
||
|
|
||
|
private void levelOrder(Node<K> node, java.util.Set<java.util.Map.Entry<K, V>> set) {
|
||
|
TreeMapNode<K,V> tmn = null;
|
||
|
if (node instanceof TreeMapNode) {
|
||
|
tmn = (TreeMapNode<K,V>)node;
|
||
|
java.util.Map.Entry<K, V> entry = new JavaCompatibleMapEntry<K, V>(tmn.id, tmn.value);
|
||
|
set.add(entry);
|
||
|
}
|
||
|
|
||
|
if (node.lesser!=null) levelOrder(node.lesser,set);
|
||
|
if (node.greater!=null) levelOrder(node.greater,set);
|
||
|
}
|
||
|
}
|
||
|
}
|