All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.kdvolder.tttree.TTTree Maven / Gradle / Ivy

There is a newer version: 0.0.2-RELEASE
Show newest version
package com.github.kdvolder.tttree;

import java.util.AbstractSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Stack;

import com.github.kdvolder.util.Assert;
import com.google.common.collect.Iterators;

public abstract class TTTree, V> implements Iterable>{

	////////////////////////////////////
	// public api
	////////////////////////////////////

	@SuppressWarnings("unchecked")
	public static , V> TTTree empty() {
		return EMPTY_TREE;
	}

	public abstract TTTree put(K k, V v);
	public final V get(K k) {
		Leaf e = getEntry(k);
		if (e!=null) {
			return e.getValue();
		}
		return null;
	}

	public abstract TTTree remove(K k);

	public boolean isEmpty() {
		return false; // good default because most nodes aren't empty.
	}

	@Override
	public Iterator> iterator() {
		if (isEmpty()) {
			Collections.emptyIterator();
		}
		return new TTTreeIterator(this);
	}

	public Set keySet() {
		return new AbstractSet() {
			private Integer size;

			@SuppressWarnings("unchecked")
			@Override
			public boolean contains(Object o) {
				if (o instanceof Comparable) {
					return TTTree.this.containsKey((K)o);
				}
				return false;
			}

			@Override
			public Iterator iterator() {
				Iterator> entries = TTTree.this.iterator();
				return Iterators.transform(entries, Map.Entry::getKey);
			}

			@Override
			public int size() {
				if (size==null) {
					this.size = TTTree.this.size();
				}
				return size;
			}
		};
	}

	public final boolean containsKey(K key) {
		return getEntry(key)!=null;
	}

	abstract Leaf getEntry(K key);

	/**
	 * Counts the elements in the {@link TTTree}.
	 * 

* WARNING: not cached... so O(n) operation. */ abstract int size(); /** * For debugging. Dump tree structure in indented format onto sysout */ public void dump() { dump(0); } ///////////////////////////////////// // implementation ///////////////////////////////////// //Some possible optimization to consider in future: // 1) Can we avoid storing/computing 'depth' in each node? // It is nice to have the depth for now to: // - execute some asserts to make sure the code doesn't break the 'equal depths' invariant // - easily determine if a tree has grown. // However it should not be necessary to track the depth. All that would be needed is for a // internal 'put/remove' operation to somehow return a boolean alongside the resulting tree to // indicate whether the new tree's depth has changed. // 2) Remove 'redundant' internal keys // See the method TTTreeTest.checkForRedundantInternalKeys @Override public abstract String toString(); protected static final TTTree[] NO_CHILDREN = {}; abstract void dump(int indent); abstract TTTree[] getChildren(); abstract int depth(); void print(int indent, Object msg) { for (int i = 0; i < indent; i++) { System.out.print(" "); } System.out.println(msg); } @SuppressWarnings({"rawtypes", "unchecked"}) private static final TTTree EMPTY_TREE = new TTTree() { @Override public TTTree put(Comparable k, Object v) { return leaf(k, v); } @Override Leaf getEntry(Comparable key) { return null; } @Override public String toString() { return "EMPTY"; } @Override public boolean isEmpty() { return true; } @Override TTTree[] getChildren() { return NO_CHILDREN; } @Override int depth() { return 0; } @Override int size() { return 0; } @Override void dump(int indent) {print(indent, this);} @Override public TTTree remove(Comparable k) {return this; } @Override public void accept(TTTreeVisitor visitor) { visitor.visit_empty(); } }; /** * Create a LEAF node which contains a single key -> value pair. */ private static , V> TTTree leaf(K k, V v) { return new Leaf(k, v); } private static class Leaf, V> extends TTTree implements Map.Entry { private final K k; private final V v; Leaf(K k, V v) { super(); this.k = k; this.v = v; } @Override Leaf getEntry(K key) { if (key.equals(k)) { return this; } return null; } @Override public TTTree put(K ik, V iv) { int compare = ik.compareTo(k); TTTree newLeaf = leaf(ik, iv); if (compare==0) { // ik == k return newLeaf; } else if (compare<0) { // ik < k return new Node2<>(newLeaf, ik, this); } else { // ik > k return new Node2<>(this, k, newLeaf); } } @Override public TTTree remove(K fk) { if (fk.equals(k)) { return empty(); } return this; } @Override protected int depth() { return 1; } @Override void dump(int indent) { print(indent, k + " = " +v); } @Override public K getKey() { return k; } @Override public V getValue() { return v; } @Override public V setValue(V value) { throw new UnsupportedOperationException("setValue"); } @SuppressWarnings("unchecked") @Override TTTree[] getChildren() { return (TTTree[]) NO_CHILDREN; } @Override int size() { return 1; } @Override public String toString() { return "["+k+" = "+v+"]"; } @Override public void accept(TTTreeVisitor visitor) { visitor.visit_leaf(k, v); } }; private static class Node2, V> extends TTTree { private final TTTree l; private final K k; private final TTTree r; private int depth; Node2(TTTree l, K k, TTTree r) { Assert.isLegalState(l.depth()==r.depth()); this.depth = l.depth()+1; this.l = l; this.k = k; this.r = r; } @Override public TTTree put(K ik, V iv) { int c = ik.compareTo(k); if (c<=0) { //ik <= k TTTree new_l = l.put(ik, iv); if (new_l.depth()>l.depth()) { //Since the tree has just grown its root must be Node2 K lk = ((Node2)new_l).k; TTTree ll = ((Node2)new_l).l; TTTree lr = ((Node2)new_l).r; return new Node3<>(ll, lk, lr, k, r); } else { //Tree remained same size return new Node2<>(new_l,k,r); } } else { //ik > k TTTree new_r = r.put(ik, iv); if (new_r.depth()>r.depth()) { //Since the tree has just grown its root must be Node2 K rk = ((Node2)new_r).k; TTTree rl = ((Node2)new_r).l; TTTree rr = ((Node2)new_r).r; return new Node3<>(l, k, rl, rk, rr); } else { //Tree remained same size return new Node2<>(l,k,new_r); } } } @Override public TTTree remove(K fk) { int c = fk.compareTo(k); if (c<=0) { // fk <= k TTTree l = this.l.remove(fk); if (this.l==l) { return this; //Avoid needless copying if tree is unchanged } else if (this.l.depth()==l.depth()) { return new Node2<>(l, k, r); } else { //l.depth shrunk if (r instanceof Node2) { Node2 r = (Node2) this.r; return new Node3<>(l, k, r.l, r.k, r.r); } else if (r instanceof Node3) { Node3 r = (Node3) this.r; return new Node2<>( new Node2<>(l, k, r.l), r.k1, new Node2<>(r.m, r.k2, r.r) ); } else { Assert.isLegalState(r instanceof Leaf); Assert.isLegalState(l.isEmpty()); return r; } } } else { // fk > k TTTree r = this.r.remove(fk); if (this.r==r) { return this; //Avoid needless copying if tree is unchanged } else if (this.r.depth()==r.depth()) { return new Node2<>(l, k, r); } else { //r.depth shrunk if (l instanceof Node2) { Node2 l = (Node2) this.l; return new Node3<>(l.l, l.k, l.r, k, r); } else if (l instanceof Node3) { Node3 l = (Node3) this.l; return new Node2<>( new Node2<>(l.l, l.k1, l.m), l.k2, new Node2<>(l.r, k, r) ); } else { Assert.isLegalState(l instanceof Leaf); Assert.isLegalState(r.isEmpty()); return l; } } } } @Override public Leaf getEntry(K fk) { int c = fk.compareTo(k); if (c<=0) { // fk <= k return l.getEntry(fk); } else { // fk > k return r.getEntry(fk); } } @Override protected int depth() { return depth; } @Override void dump(int indent) { l.dump(indent+1); print(indent, k); r.dump(indent+1); } @SuppressWarnings("unchecked") @Override TTTree[] getChildren() { return new TTTree[] {l, r}; } @Override int size() { return l.size() + r.size(); } @Override public String toString() { return "Node2["+depth+"]("+k+")"; } @Override public void accept(TTTreeVisitor visitor) { visitor.visit_2node(l, k, r); } } private static class Node3, V> extends TTTree { private int depth; final TTTree l; final K k1; final TTTree m; final K k2; final TTTree r; public Node3(TTTree l, K k1, TTTree m, K k2, TTTree r) { Assert.isLegalState(l.depth()==m.depth()); Assert.isLegalState(l.depth()==r.depth()); this.depth = l.depth()+1; this.l = l; this.k1 = k1; this.m = m; this.k2 = k2; this.r = r; } @Override public TTTree put(K k, V v) { int c = k.compareTo(k1); if (c<=0) { //k <= k1 final TTTree l = this.l.put(k, v); if (l.depth()>this.l.depth()) { //The tree has just grown //split ourself into a new Node2. return new Node2<>( l, k1, new Node2<>(m, k2, r) ); } else { //Tree remained same size return new Node3<>(l, k1, m, k2, r); } } else { // k1 < k c = k.compareTo(k2); if (c<=0) { //k1 < k <= k2 final TTTree m = this.m.put(k, v); if (m.depth()>this.m.depth()) { //Since the tree has just grown its root *must* be Node2 K mk = ((Node2)m).k; TTTree ml = ((Node2)m).l; TTTree mr = ((Node2)m).r; return new Node2<>( new Node2<>(l, k1, ml), mk, new Node2<>(mr, k2, r) ); } else { //Tree remained same size return new Node3<>(l, k1, m, k2, r); } } else { //k2 < k final TTTree r = this.r.put(k, v); if (r.depth()>this.r.depth()) { //The tree has just grown //split ourself into a new Node2. return new Node2<>( new Node2<>(l, k1, m), k2, r ); } else { //Tree remained same size return new Node3<>(l, k1, m, k2, r); } } } } @Override public TTTree remove(K fk) { int c = fk.compareTo(k1); if (c<=0) { //fk <= k1 TTTree l = this.l.remove(fk); if (l==this.l) { return this; } else if (l.depth()==this.l.depth()) { return new Node3<>(l, k1 ,m, k2, r); } else { //shrunk l if (l.isEmpty()) { return new Node2<>(m, k2, r); } else if (m instanceof Node2) { Node2 m = (Node2) this.m; return new Node2<>( new Node3<>(l, k1, m.l, m.k, m.r), k2, r ); } else { //m instanceof Node3 Node3 m = (Node3) this.m; return new Node3<>( new Node2<>(l, k1, m.l), m.k1, new Node2<>(m.m, m.k2, m.r), k2, r ); } } } else { //k1 < fk c = fk.compareTo(k2); if (c<=0) { //k1 < fk <= k2 TTTree m = this.m.remove(fk); if (m==this.m) { return this; } else if (m.depth()==this.m.depth()) { return new Node3<>(l, k1, m, k2, r); } else { //shrunk if (m.isEmpty()) { return new Node2<>(l, k1, r); } else if (l instanceof Node2) { Node2 l = (Node2) this.l; return new Node2<>( new Node3<>(l.l, l.k, l.r, k1, m), k2, r ); } else { //l instanceof Node3 Node3 l = (Node3) this.l; return new Node3<>( new Node2<>(l.l, l.k1, l.m), l.k2, new Node2<>(l.r, k1, m), k2, r ); } } } else { //k2 < fk TTTree r = this.r.remove(fk); if (r==this.r) { return this; } else if (r.depth()==this.r.depth()) { return new Node3<>(l, k1, m, k2, r); } else { //shrunk if (r.isEmpty()) { return new Node2<>(l, k1, m); } else if (m instanceof Node2) { Node2 m = (Node2)this.m; return new Node2<>( l, k1, new Node3<>(m.l, m.k, m.r, k2, r) ); } else { //m instanceof Node3 Node3 m = (Node3) this.m; return new Node3<>( l, k1, new Node2<>(m.l, m.k1, m.m), m.k2, new Node2<>(m.r, k2, r) ); } } } } } @Override public Leaf getEntry(K k) { int c = k.compareTo(k1); if (c<=0) { //k <= k1 return l.getEntry(k); } else { // k1 < k c = k.compareTo(k2); if (c<=0) { //k1 < k <= k2 return m.getEntry(k); } else { //k2 < k return r.getEntry(k); } } } @Override protected int depth() { return depth; } @Override void dump(int indent) { l.dump(indent+1); print(indent, k1); m.dump(indent+1); print(indent, k2); r.dump(indent+1); } @SuppressWarnings("unchecked") @Override TTTree[] getChildren() { return new TTTree[] {l, m, r}; } @Override int size() { return l.size() + m.size() + r.size(); } @Override public String toString() { return "Node3["+depth+"]("+k1+", "+k2+")"; } @Override public void accept(TTTreeVisitor visitor) { visitor.visit_3node(l, k1, m, k2, r); } } private class TTTreeIterator implements Iterator> { Stack> stack = new Stack<>(); TTTreeIterator(TTTree tree) { if (!tree.isEmpty()) stack.push(tree); } @Override public boolean hasNext() { return !stack.isEmpty(); } @Override public Entry next() { while(!stack.isEmpty()) { TTTree node = stack.pop(); if (node instanceof Leaf) { return (Leaf)node; } else { TTTree[] children = node.getChildren(); for (int i = children.length-1; i >= 0; i--) { TTTree c = children[i]; if (!c.isEmpty()) { stack.push(children[i]); } } } } throw new NoSuchElementException(); } } /** * Useful to perform various traversals / analysis on the tree. */ public abstract void accept(TTTreeVisitor visitor); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy