612 lines
16 KiB
Java
612 lines
16 KiB
Java
package net.abhinavsarkar.algorist;
|
|
|
|
import java.io.IOException;
|
|
import java.util.Iterator;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.Optional;
|
|
import java.util.Stack;
|
|
|
|
public class AVLTree<K extends Comparable<K>,V> implements Iterable<AVLTree.Entry<K,V>>
|
|
{
|
|
private Node<K,V> root;
|
|
|
|
public AVLTree(boolean rebalance) {
|
|
root = EmptyNode.instance(rebalance);
|
|
}
|
|
|
|
public void insert(K key, V val) {
|
|
this.root = this.root.insert(key, val);
|
|
}
|
|
|
|
public Optional<V> get(K key) {
|
|
return this.root.get(key);
|
|
}
|
|
|
|
public void delete(K key) {
|
|
this.root = this.root.delete(key);
|
|
}
|
|
|
|
public Optional<K> minimum() {
|
|
return this.root.minimum();
|
|
}
|
|
|
|
public Optional<K> maximum() {
|
|
return this.root.maximum();
|
|
}
|
|
|
|
public Optional<K> successor(K key) {
|
|
return this.root.successor(key);
|
|
}
|
|
|
|
public Optional<K> predecessor(K key) {
|
|
return this.root.predecessor(key);
|
|
}
|
|
|
|
public String toString() {
|
|
return this.root.toStringBuilder(0).toString();
|
|
}
|
|
|
|
public void renderToPNG(String fileName) throws IOException
|
|
{
|
|
AVLTreeGraphvizVisitor visitor = new AVLTreeGraphvizVisitor("AVLTree");
|
|
this.root.accept(visitor);
|
|
visitor.renderToPNG(fileName);
|
|
}
|
|
|
|
@Override
|
|
public Iterator<Entry<K, V>> iterator()
|
|
{
|
|
return new InOrderIterator<>(this.root);
|
|
}
|
|
|
|
private interface Node<K extends Comparable<K>, V>
|
|
{
|
|
int height();
|
|
Node<K,V> insert(K key, V val);
|
|
Optional<V> get(K key);
|
|
Node<K,V> delete(K key);
|
|
Optional<K> minimum();
|
|
Optional<K> maximum();
|
|
Optional<K> successor(K key);
|
|
Optional<K> predecessor(K key);
|
|
boolean isEmpty();
|
|
StringBuilder toStringBuilder(int level);
|
|
boolean checkIfBST();
|
|
boolean checkIfAVLT();
|
|
Optional<ValueNode<K,V>> accept(Visitor visitor);
|
|
|
|
default ValueNode<K,V> toValueNode() {
|
|
if (this.isEmpty()) {
|
|
throw new UnsupportedOperationException();
|
|
} else {
|
|
return (ValueNode<K, V>) this;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class ValueNode<K extends Comparable<K>,V> implements Node<K, V>
|
|
{
|
|
private final boolean rebalance;
|
|
private K key;
|
|
private V val;
|
|
private int height;
|
|
private Node<K,V> left;
|
|
private Node<K,V> right;
|
|
|
|
ValueNode(K key, V val, boolean rebalance) {
|
|
this.rebalance = rebalance;
|
|
if (key == null) {
|
|
throw new IllegalArgumentException("Key cannot be null");
|
|
}
|
|
if (val == null) {
|
|
throw new IllegalArgumentException("Value cannot be null");
|
|
}
|
|
|
|
this.key = key;
|
|
this.val = val;
|
|
this.height = 1;
|
|
left = EmptyNode.instance(rebalance);
|
|
right = EmptyNode.instance(rebalance);
|
|
}
|
|
|
|
@Override
|
|
public int height()
|
|
{
|
|
return height;
|
|
}
|
|
|
|
public int balance()
|
|
{
|
|
return this.left.height() - this.right.height();
|
|
}
|
|
|
|
@Override
|
|
public boolean isEmpty()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Optional<V> get(K key)
|
|
{
|
|
if (this.key.equals(key)) {
|
|
return Optional.of(val);
|
|
} else if (this.key.compareTo(key) > 0) {
|
|
return this.left.get(key);
|
|
} else {
|
|
return this.right.get(key);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Node<K, V> insert(K key, V val)
|
|
{
|
|
if (this.key.equals(key)) {
|
|
this.val = val;
|
|
return this;
|
|
} else if (this.key.compareTo(key) > 0) {
|
|
this.left = this.left.insert(key, val);
|
|
} else {
|
|
this.right = this.right.insert(key, val);
|
|
}
|
|
|
|
return this.rebalance();
|
|
}
|
|
|
|
@Override
|
|
public Node<K,V> delete(K key)
|
|
{
|
|
if (this.key.equals(key)) {
|
|
Node<K, V> node = doDelete();
|
|
if (node.isEmpty()) {
|
|
return node;
|
|
}
|
|
return node.toValueNode().rebalance();
|
|
} else if (this.key.compareTo(key) > 0) {
|
|
this.left = this.left.delete(key);
|
|
} else {
|
|
this.right = this.right.delete(key);
|
|
}
|
|
|
|
return this.rebalance();
|
|
}
|
|
|
|
private Node<K, V> doDelete()
|
|
{
|
|
if (this.left.isEmpty())
|
|
{
|
|
return this.right.isEmpty() ? EmptyNode.instance(rebalance) : this.right;
|
|
}
|
|
else if (this.right.isEmpty())
|
|
{
|
|
return this.left;
|
|
}
|
|
else
|
|
{
|
|
ValueNode<K, V> node = deleteSuccessor();
|
|
this.key = node.key;
|
|
this.val = node.val;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
private ValueNode<K, V> deleteSuccessor()
|
|
{
|
|
Node<K,V> current = this.right;
|
|
ValueNode<K,V> successor = this.right.toValueNode();
|
|
ValueNode<K,V> successorParent = this.toValueNode();
|
|
do
|
|
{
|
|
ValueNode<K, V> cur = current.toValueNode();
|
|
Node<K, V> curLeft = cur.left;
|
|
if (!curLeft.isEmpty()) {
|
|
successor = curLeft.toValueNode();
|
|
successorParent = cur;
|
|
}
|
|
current = curLeft;
|
|
} while (!current.isEmpty());
|
|
|
|
successorParent.delete(successor.key);
|
|
return successor;
|
|
}
|
|
|
|
private ValueNode<K, V> rebalance()
|
|
{
|
|
this.resetHeight();
|
|
if (!rebalance) {
|
|
return this;
|
|
}
|
|
|
|
int balance = this.balance();
|
|
if (balance > 1) {
|
|
ValueNode<K, V> l = this.left.toValueNode();
|
|
if (l.balance() <= 0) { // left-right case
|
|
this.left = l.rotateLeft();
|
|
}
|
|
return this.rotateRight();
|
|
} else if (balance < -1) {
|
|
ValueNode<K, V> r = this.right.toValueNode();
|
|
if (r.balance() > 0) { // right-left case
|
|
this.right = r.rotateRight();
|
|
}
|
|
return this.rotateLeft();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
private ValueNode<K,V> rotateLeft()
|
|
{
|
|
ValueNode<K, V> r = this.right.toValueNode();
|
|
this.right = r.left;
|
|
r.left = this;
|
|
|
|
this.resetHeight();
|
|
r.resetHeight();
|
|
|
|
return r;
|
|
}
|
|
|
|
private ValueNode<K, V> rotateRight()
|
|
{
|
|
ValueNode<K, V> l = this.left.toValueNode();
|
|
this.left = l.right;
|
|
l.right = this;
|
|
|
|
this.resetHeight();
|
|
l.resetHeight();
|
|
|
|
return l;
|
|
}
|
|
|
|
private void resetHeight()
|
|
{
|
|
this.height = Math.max(left.height(), right.height()) + 1;
|
|
}
|
|
|
|
@Override
|
|
public Optional<K> minimum()
|
|
{
|
|
return Optional.of(this.left.minimum().orElse(this.key));
|
|
}
|
|
|
|
@Override
|
|
public Optional<K> maximum()
|
|
{
|
|
return Optional.of(this.right.maximum().orElse(this.key));
|
|
}
|
|
|
|
@Override
|
|
public Optional<K> successor(K key)
|
|
{
|
|
if (this.key.compareTo(key) > 0) {
|
|
return Optional.of(this.left.successor(key).orElse(this.key));
|
|
} else {
|
|
return this.right.successor(key);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Optional<K> predecessor(K key)
|
|
{
|
|
if (this.key.compareTo(key) < 0) {
|
|
return Optional.of(this.right.predecessor(key).orElse(this.key));
|
|
} else {
|
|
return this.left.predecessor(key);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean checkIfBST()
|
|
{
|
|
return this.left.checkIfBST() && this.right.checkIfBST()
|
|
&& isKeyMoreThanLeftKey() && isKeyLessThanRightKey();
|
|
}
|
|
|
|
@Override
|
|
public boolean checkIfAVLT()
|
|
{
|
|
int balance = balance();
|
|
return this.left.checkIfAVLT() && this.right.checkIfAVLT() && balance >= -1 && balance <= 1;
|
|
}
|
|
|
|
private boolean isKeyMoreThanLeftKey()
|
|
{
|
|
return this.left.isEmpty() || this.key.compareTo(this.left.toValueNode().key) > 0;
|
|
}
|
|
|
|
private boolean isKeyLessThanRightKey()
|
|
{
|
|
return this.right.isEmpty() || this.key.compareTo(this.right.toValueNode().key) < 0;
|
|
}
|
|
|
|
@Override
|
|
public StringBuilder toStringBuilder(int level) {
|
|
return new StringBuilder().append(gutter(level))
|
|
.append(height)
|
|
.append('<')
|
|
.append(key)
|
|
.append(':')
|
|
.append(val)
|
|
.append(">\n")
|
|
.append(this.left.toStringBuilder(level + 1))
|
|
.append(this.right.toStringBuilder(level + 1));
|
|
}
|
|
|
|
@Override
|
|
public String toString()
|
|
{
|
|
return "{" + key + ':' + val + '}';
|
|
}
|
|
|
|
@Override
|
|
public Optional<ValueNode<K,V>> accept(Visitor visitor)
|
|
{
|
|
String name = this.getName();
|
|
visitor.visitNode(name);
|
|
this.left.accept(visitor).ifPresent(n -> visitor.visitLink(name, n.getName()));
|
|
this.right.accept(visitor).ifPresent(n -> visitor.visitLink(name, n.getName()));
|
|
return Optional.of(this);
|
|
}
|
|
|
|
private String getName()
|
|
{
|
|
return this.height + ":" + this.key;
|
|
}
|
|
}
|
|
|
|
private static class EmptyNode<K extends Comparable<K>,V> implements Node<K,V>
|
|
{
|
|
@SuppressWarnings("rawtypes")
|
|
private static final EmptyNode TRUE_INSTANCE = new EmptyNode(true);
|
|
|
|
@SuppressWarnings("rawtypes")
|
|
private static final EmptyNode FALSE_INSTANCE = new EmptyNode(false);
|
|
|
|
private final boolean rebalance;
|
|
|
|
private EmptyNode(boolean rebalance)
|
|
{
|
|
this.rebalance = rebalance;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public static <K extends Comparable<K>,V> EmptyNode<K,V> instance(boolean rebalance) {
|
|
return rebalance ? TRUE_INSTANCE : FALSE_INSTANCE;
|
|
}
|
|
|
|
@Override
|
|
public int height()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public Node<K, V> insert(K key, V val)
|
|
{
|
|
return new ValueNode<>(key, val, rebalance);
|
|
}
|
|
|
|
@Override
|
|
public boolean isEmpty()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public Optional<V> get(K key)
|
|
{
|
|
return Optional.empty();
|
|
}
|
|
|
|
@Override
|
|
public Node<K, V> delete(K key) {
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Optional<K> minimum()
|
|
{
|
|
return Optional.empty();
|
|
}
|
|
|
|
@Override
|
|
public Optional<K> maximum()
|
|
{
|
|
return Optional.empty();
|
|
}
|
|
|
|
public Optional<K> successor(K key) {
|
|
return Optional.empty();
|
|
}
|
|
|
|
public Optional<K> predecessor(K key) {
|
|
return Optional.empty();
|
|
}
|
|
|
|
@Override
|
|
public boolean checkIfBST()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean checkIfAVLT()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public StringBuilder toStringBuilder(int level)
|
|
{
|
|
return new StringBuilder().append(gutter(level)).append("<NULL>\n");
|
|
}
|
|
|
|
@Override
|
|
public String toString()
|
|
{
|
|
return "<NULL>";
|
|
}
|
|
|
|
@Override
|
|
public Optional<ValueNode<K, V>> accept(Visitor visitor) {
|
|
return Optional.empty();
|
|
}
|
|
}
|
|
|
|
private static StringBuilder gutter(int times) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int i = 0; i < times; i++)
|
|
{
|
|
sb.append('│');
|
|
}
|
|
return sb.append("├ ");
|
|
}
|
|
|
|
public static class Entry<K, V> {
|
|
private final K key;
|
|
private final V val;
|
|
|
|
public Entry(K key, V val) {
|
|
this.key = key;
|
|
this.val = val;
|
|
}
|
|
|
|
public K getKey()
|
|
{
|
|
return key;
|
|
}
|
|
|
|
public V getVal()
|
|
{
|
|
return val;
|
|
}
|
|
|
|
public String toString() {
|
|
return "<" + key + "," + val + ">";
|
|
}
|
|
}
|
|
|
|
private static class InOrderIterator<K extends Comparable<K>, V> implements Iterator<Entry<K, V>>
|
|
{
|
|
private final Stack<Node<K, V>> stack = new Stack<>();
|
|
|
|
public InOrderIterator(Node<K, V> node)
|
|
{
|
|
pushToStack(node);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasNext()
|
|
{
|
|
return !(this.stack.isEmpty() || this.stack.peek().isEmpty());
|
|
}
|
|
|
|
/*
|
|
inorder(a):
|
|
if a.left != null:
|
|
yield inorder(a.left)
|
|
yield a.val
|
|
if a.right != null:
|
|
yield inorder(a.right)
|
|
*/
|
|
@Override
|
|
public Entry<K, V> next()
|
|
{
|
|
if (!hasNext()) {
|
|
throw new NoSuchElementException();
|
|
}
|
|
|
|
ValueNode<K, V> vNode = this.stack.pop().toValueNode();
|
|
pushToStack(vNode.right);
|
|
|
|
return new Entry<>(vNode.key, vNode.val);
|
|
}
|
|
|
|
private void pushToStack(Node<K, V> node)
|
|
{
|
|
Node<K, V> current = node;
|
|
while (!current.isEmpty())
|
|
{
|
|
this.stack.push(current);
|
|
current = current.toValueNode().left;
|
|
}
|
|
}
|
|
}
|
|
|
|
public interface Visitor
|
|
{
|
|
void visitNode(String nodeName);
|
|
void visitLink(String parentNodeName, String childNodeName);
|
|
}
|
|
|
|
public static void main(String[] args) throws IOException
|
|
{
|
|
AVLTree<String, String> bst = new AVLTree<>(true);
|
|
bst.insert("a", "barista");
|
|
bst.insert("b", "duck");
|
|
bst.insert("c", "carpool");
|
|
bst.insert("d", "duck");
|
|
bst.insert("e", "carpool");
|
|
bst.insert("f", "barista");
|
|
bst.insert("g", "duck");
|
|
bst.insert("h", "abhinav");
|
|
bst.insert("i", "abhinav");
|
|
bst.insert("j", "duck");
|
|
bst.insert("k", "carpool");
|
|
bst.insert("l", "carpool");
|
|
bst.insert("m", "abhinav");
|
|
bst.insert("n", "duck");
|
|
bst.insert("o", "abhinav");
|
|
bst.insert("p", "abhinav");
|
|
bst.insert("q", "carpool");
|
|
bst.insert("r", "barista");
|
|
bst.insert("s", "duck");
|
|
bst.insert("t", "barista");
|
|
bst.insert("u", "carpool");
|
|
bst.insert("v", "abhinav");
|
|
bst.insert("w", "duck");
|
|
bst.insert("x", "barista");
|
|
bst.insert("y", "barista");
|
|
bst.insert("z", "duck");
|
|
|
|
System.out.println(bst);
|
|
System.out.println(bst.root.checkIfBST());
|
|
System.out.println(bst.root.checkIfAVLT());
|
|
bst.renderToPNG("AVLTree.png");
|
|
|
|
System.out.println(bst.get("a"));
|
|
System.out.println(bst.get("b"));
|
|
System.out.println(bst.get("c"));
|
|
System.out.println(bst.get("d"));
|
|
System.out.println(bst.get("A"));
|
|
|
|
bst.delete("q");
|
|
System.out.println(bst.root.checkIfBST());
|
|
System.out.println(bst.root.checkIfAVLT());
|
|
bst.delete("r");
|
|
System.out.println(bst.root.checkIfBST());
|
|
System.out.println(bst.root.checkIfAVLT());
|
|
bst.delete("k");
|
|
System.out.println(bst.root.checkIfBST());
|
|
System.out.println(bst.root.checkIfAVLT());
|
|
bst.delete("c");
|
|
System.out.println(bst.root.checkIfBST());
|
|
System.out.println(bst.root.checkIfAVLT());
|
|
System.out.println(bst);
|
|
|
|
System.out.println(bst.minimum());
|
|
System.out.println(bst.maximum());
|
|
System.out.println(bst.successor("a"));
|
|
System.out.println(bst.successor("z"));
|
|
System.out.println(bst.successor("q"));
|
|
|
|
System.out.println(bst.predecessor("a"));
|
|
System.out.println(bst.predecessor("z"));
|
|
System.out.println(bst.predecessor("q"));
|
|
|
|
for (Entry<String,String> entry: bst) {
|
|
System.out.println(entry.key);
|
|
}
|
|
}
|
|
}
|