Adds AVL tree

This commit is contained in:
Abhinav Sarkar 2019-07-04 17:23:39 +05:30
parent b339299d15
commit 6f0227e188
2 changed files with 560 additions and 3 deletions

View File

@ -0,0 +1,556 @@
package net.abhinavsarkar.algorist;
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 = EmptyNode.instance();
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();
}
@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();
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 K key;
private V val;
private int height;
private Node<K,V> left = EmptyNode.instance();
private Node<K,V> right = EmptyNode.instance();
ValueNode(K key, V val) {
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;
}
@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() : 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();
int balance = this.balance();
if (balance > 1) {
ValueNode<K, V> l = this.left.toValueNode();
if (l.balance() > 0) { // left-left case
return this.rotateRight();
} else { // 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();
} else { // right-right case
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 + '}';
}
}
private static class EmptyNode<K extends Comparable<K>,V> implements Node<K,V>
{
@SuppressWarnings("rawtypes")
private static final EmptyNode INSTANCE = new EmptyNode();
public static <K extends Comparable<K>,V> EmptyNode<K,V> instance() {
return INSTANCE;
}
@Override
public int height()
{
return 0;
}
@Override
public Node<K, V> insert(K key, V val)
{
return new ValueNode<>(key, val);
}
@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>";
}
}
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 K key;
private 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 static void main(String[] args)
{
AVLTree<String, String> bst = new AVLTree<>();
bst.insert("q", "barista");
bst.insert("y", "duck");
bst.insert("k", "carpool");
bst.insert("p", "duck");
bst.insert("c", "carpool");
bst.insert("e", "barista");
bst.insert("d", "duck");
bst.insert("f", "abhinav");
bst.insert("j", "abhinav");
bst.insert("t", "duck");
bst.insert("s", "carpool");
bst.insert("g", "carpool");
bst.insert("v", "abhinav");
bst.insert("h", "duck");
bst.insert("r", "abhinav");
bst.insert("n", "abhinav");
bst.insert("w", "carpool");
bst.insert("u", "barista");
bst.insert("z", "duck");
bst.insert("b", "barista");
bst.insert("o", "carpool");
bst.insert("a", "abhinav");
bst.insert("l", "duck");
bst.insert("m", "barista");
bst.insert("i", "barista");
bst.insert("x", "duck");
System.out.println(bst);
System.out.println(bst.root.checkIfBST());
System.out.println(bst.root.checkIfAVLT());
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);
}
}
}

View File

@ -18,7 +18,7 @@ public class BinarySearchTree<K extends Comparable<K>,V> implements Iterable<Bin
}
public void delete(K key) {
this.root.delete(key);
this.root = this.root.delete(key);
}
public Optional<K> minimum() {
@ -155,7 +155,7 @@ public class BinarySearchTree<K extends Comparable<K>,V> implements Iterable<Bin
{
Node<K,V> current = this.right;
ValueNode<K,V> successor = this.right.toValueNode();
ValueNode<K,V> successorParent = this.right.toValueNode();
ValueNode<K,V> successorParent = this.toValueNode();
do
{
ValueNode<K, V> cur = current.toValueNode();
@ -167,10 +167,11 @@ public class BinarySearchTree<K extends Comparable<K>,V> implements Iterable<Bin
current = curLeft;
} while (!current.isEmpty());
successorParent.left = EmptyNode.instance();
successorParent.delete(successor.key);
return successor;
}
@Override
public Optional<K> minimum()
{