package net.abhinavsarkar.algorist; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Optional; import java.util.Stack; public class BinarySearchTree,V> implements Iterable> { private Node root = EmptyNode.instance(); public void insert(K key, V val) { this.root = this.root.insert(key, val); } public Optional get(K key) { return this.root.get(key); } public void delete(K key) { this.root.delete(key); } public Optional minimum() { return this.root.minimum(); } public Optional maximum() { return this.root.maximum(); } public Optional successor(K key) { return this.root.successor(key); } public Optional predecessor(K key) { return this.root.predecessor(key); } public String toString() { return this.root.toStringBuilder(0).toString(); } @Override public Iterator> iterator() { return new PreOrderIterator<>(this.root); } private interface Node, V> { Node insert(K key, V val); Optional get(K key); Node delete(K key); Optional minimum(); Optional maximum(); Optional successor(K key); Optional predecessor(K key); boolean isEmpty(); StringBuilder toStringBuilder(int level); boolean checkIfBST(); default ValueNode toValueNode() { if (this.isEmpty()) { throw new UnsupportedOperationException(); } else { return (ValueNode) this; } } } private static class ValueNode,V> implements Node { private K key; private V val; private Node left = EmptyNode.instance(); private Node 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; } @Override public boolean isEmpty() { return false; } @Override public Node insert(K key, V val) { if (this.key.equals(key)) { this.val = val; } else if (this.key.compareTo(key) > 0) { this.left = this.left.insert(key, val); } else { this.right = this.right.insert(key, val); } return this; } @Override public Optional 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 delete(K key) { if (this.key.equals(key)) { return doDelete(); } else if (this.key.compareTo(key) > 0) { this.left = this.left.delete(key); } else { this.right = this.right.delete(key); } return this; } private Node doDelete() { if (this.left.isEmpty()) { return this.right.isEmpty() ? EmptyNode.instance() : this.right; } else if (this.right.isEmpty()) { return this.left; } else { ValueNode node = deleteSuccessor(); this.key = node.key; this.val = node.val; return this; } } private ValueNode deleteSuccessor() { Node current = this.right; ValueNode successor = this.right.toValueNode(); ValueNode successorParent = this.right.toValueNode(); do { ValueNode cur = current.toValueNode(); Node curLeft = cur.left; if (!curLeft.isEmpty()) { successor = curLeft.toValueNode(); successorParent = cur; } current = curLeft; } while (!current.isEmpty()); successorParent.left = EmptyNode.instance(); return successor; } @Override public Optional minimum() { return Optional.of(this.left.minimum().orElse(this.key)); } @Override public Optional maximum() { return Optional.of(this.right.maximum().orElse(this.key)); } @Override public Optional 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 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(); } 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('<') .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,V> implements Node { @SuppressWarnings("rawtypes") private static final EmptyNode INSTANCE = new EmptyNode(); public static ,V> EmptyNode instance() { return INSTANCE; } @Override public Node insert(K key, V val) { return new ValueNode<>(key, val); } @Override public boolean isEmpty() { return true; } @Override public Optional get(K key) { return Optional.empty(); } @Override public Node delete(K key) { return this; } @Override public Optional minimum() { return Optional.empty(); } @Override public Optional maximum() { return Optional.empty(); } public Optional successor(K key) { return Optional.empty(); } public Optional predecessor(K key) { return Optional.empty(); } @Override public boolean checkIfBST() { return true; } @Override public StringBuilder toStringBuilder(int level) { return new StringBuilder().append(gutter(level)).append("\n"); } @Override public String toString() { return ""; } } 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 { 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, V> implements Iterator> { private final Stack> stack = new Stack<>(); public InOrderIterator(Node 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 next() { if (!hasNext()) { throw new NoSuchElementException(); } ValueNode vNode = this.stack.pop().toValueNode(); pushToStack(vNode.right); return new Entry<>(vNode.key, vNode.val); } private void pushToStack(Node node) { Node current = node; while (!current.isEmpty()) { this.stack.push(current); current = current.toValueNode().left; } } } private static class PreOrderIterator, V> implements Iterator> { private final Stack> stack = new Stack<>(); private Node current; public PreOrderIterator(Node node) { current = node; } @Override public boolean hasNext() { return !this.current.isEmpty(); } /* preorder(a): yield a.val if a.left != null: yield preorder(a.left) if a.right != null: yield preorder(a.right) */ @Override public Entry next() { if (!hasNext()) { throw new NoSuchElementException(); } ValueNode vNode = current.toValueNode(); if (!vNode.left.isEmpty()) { stack.push(current); current = vNode.left; } else if (!vNode.right.isEmpty()) { current = vNode.right; } else if (stack.isEmpty()){ current = EmptyNode.instance(); } else { do { Node node = stack.pop(); current = node.toValueNode().right; } while (current.isEmpty()); } return new Entry<>(vNode.key, vNode.val); } } public static void main(String[] args) { BinarySearchTree bst = new BinarySearchTree<>(); 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.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"); bst.delete("r"); bst.delete("c"); System.out.println(bst); System.out.println(bst.root.checkIfBST()); 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 entry: bst) { System.out.println(entry.key); } } }