| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- import java.util.ArrayList;
- import java.util.List;
- /**
- * An AVL tree is a self-balancing binary search tree, and it was the first such
- * data structure to be invented. In an AVL tree, the heights of the two child
- * subtrees of any node differ by at most one. AVL trees are often compared with
- * red-black trees because they support the same set of operations and because
- * red-black trees also take O(log n) time for the basic operations. Because AVL
- * trees are more rigidly balanced, they are faster than red-black trees for
- * lookup intensive applications. However, red-black trees are faster for
- * insertion and removal.
- * <p>
- * @see <a href="https://en.wikipedia.org/wiki/AVL_tree">AVL Tree (Wikipedia)</a>
- * <br>
- * @author Justin Wetherell <phishman3579@gmail.com>
- */
- public class AVLTree<T extends Comparable<T>> extends BinarySearchTree<T> {
- private enum Balance {
- LEFT_LEFT, LEFT_RIGHT, RIGHT_LEFT, RIGHT_RIGHT
- }
- /**
- * Default constructor.
- */
- public AVLTree() {
- this.creator = new BinarySearchTree.INodeCreator<T>() {
- /**
- * {@inheritDoc}
- */
- @Override
- public BinarySearchTree.Node<T> createNewNode(BinarySearchTree.Node<T> parent, T id) {
- return (new AVLNode<T>(parent, id));
- }
- };
- }
- /**
- * Constructor with external Node creator.
- */
- public AVLTree(INodeCreator<T> creator) {
- super(creator);
- }
- /**
- * {@inheritDoc}
- */
- @Override
- protected Node<T> addValue(T id) {
- Node<T> nodeToReturn = super.addValue(id);
- AVLNode<T> nodeAdded = (AVLNode<T>) nodeToReturn;
- nodeAdded.updateHeight();
- balanceAfterInsert(nodeAdded);
- nodeAdded = (AVLNode<T>) nodeAdded.parent;
- while (nodeAdded != null) {
- int h1 = nodeAdded.height;
- nodeAdded.updateHeight();
- balanceAfterInsert(nodeAdded);
- // If height before and after balance is the same, stop going up the tree
- int h2 = nodeAdded.height;
- if (h1==h2)
- break;
- nodeAdded = (AVLNode<T>) nodeAdded.parent;
- }
- return nodeToReturn;
- }
- /**
- * Balance the tree according to the AVL post-insert algorithm.
- *
- * @param node
- * Root of tree to balance.
- */
- private void balanceAfterInsert(AVLNode<T> node) {
- int balanceFactor = node.getBalanceFactor();
- if (balanceFactor > 1 || balanceFactor < -1) {
- AVLNode<T> child = null;
- Balance balance = null;
- if (balanceFactor < 0) {
- child = (AVLNode<T>) node.lesser;
- balanceFactor = child.getBalanceFactor();
- if (balanceFactor < 0)
- balance = Balance.LEFT_LEFT;
- else
- balance = Balance.LEFT_RIGHT;
- } else {
- child = (AVLNode<T>) node.greater;
- balanceFactor = child.getBalanceFactor();
- if (balanceFactor < 0)
- balance = Balance.RIGHT_LEFT;
- else
- balance = Balance.RIGHT_RIGHT;
- }
- if (balance == Balance.LEFT_RIGHT) {
- // Left-Right (Left rotation, right rotation)
- rotateLeft(child);
- rotateRight(node);
- } else if (balance == Balance.RIGHT_LEFT) {
- // Right-Left (Right rotation, left rotation)
- rotateRight(child);
- rotateLeft(node);
- } else if (balance == Balance.LEFT_LEFT) {
- // Left-Left (Right rotation)
- rotateRight(node);
- } else {
- // Right-Right (Left rotation)
- rotateLeft(node);
- }
- child.updateHeight();
- node.updateHeight();
- }
- }
- /**
- * {@inheritDoc}
- */
- @Override
- protected Node<T> removeValue(T value) {
- // Find node to remove
- Node<T> nodeToRemoved = this.getNode(value);
- if (nodeToRemoved==null)
- return null;
- // Find the replacement node
- Node<T> replacementNode = this.getReplacementNode(nodeToRemoved);
- // Find the parent of the replacement node to re-factor the height/balance of the tree
- AVLNode<T> nodeToRefactor = null;
- if (replacementNode != null)
- nodeToRefactor = (AVLNode<T>) replacementNode.parent;
- if (nodeToRefactor == null)
- nodeToRefactor = (AVLNode<T>) nodeToRemoved.parent;
- if (nodeToRefactor != null && nodeToRefactor == nodeToRemoved)
- nodeToRefactor = (AVLNode<T>) replacementNode;
- // Replace the node
- replaceNodeWithNode(nodeToRemoved, replacementNode);
- // Re-balance the tree all the way up the tree
- while (nodeToRefactor != null) {
- nodeToRefactor.updateHeight();
- balanceAfterDelete(nodeToRefactor);
- nodeToRefactor = (AVLNode<T>) nodeToRefactor.parent;
- }
- return nodeToRemoved;
- }
- /**
- * Balance the tree according to the AVL post-delete algorithm.
- *
- * @param node
- * Root of tree to balance.
- */
- private void balanceAfterDelete(AVLNode<T> node) {
- int balanceFactor = node.getBalanceFactor();
- if (balanceFactor == -2 || balanceFactor == 2) {
- if (balanceFactor == -2) {
- AVLNode<T> ll = (AVLNode<T>) node.lesser.lesser;
- int lesser = (ll != null) ? ll.height : 0;
- AVLNode<T> lr = (AVLNode<T>) node.lesser.greater;
- int greater = (lr != null) ? lr.height : 0;
- if (lesser >= greater) {
- rotateRight(node);
- node.updateHeight();
- if (node.parent != null)
- ((AVLNode<T>) node.parent).updateHeight();
- } else {
- rotateLeft(node.lesser);
- rotateRight(node);
- AVLNode<T> p = (AVLNode<T>) node.parent;
- if (p.lesser != null)
- ((AVLNode<T>) p.lesser).updateHeight();
- if (p.greater != null)
- ((AVLNode<T>) p.greater).updateHeight();
- p.updateHeight();
- }
- } else if (balanceFactor == 2) {
- AVLNode<T> rr = (AVLNode<T>) node.greater.greater;
- int greater = (rr != null) ? rr.height : 0;
- AVLNode<T> rl = (AVLNode<T>) node.greater.lesser;
- int lesser = (rl != null) ? rl.height : 0;
- if (greater >= lesser) {
- rotateLeft(node);
- node.updateHeight();
- if (node.parent != null)
- ((AVLNode<T>) node.parent).updateHeight();
- } else {
- rotateRight(node.greater);
- rotateLeft(node);
- AVLNode<T> p = (AVLNode<T>) node.parent;
- if (p.lesser != null)
- ((AVLNode<T>) p.lesser).updateHeight();
- if (p.greater != null)
- ((AVLNode<T>) p.greater).updateHeight();
- p.updateHeight();
- }
- }
- }
- }
- /**
- * {@inheritDoc}
- */
- @Override
- protected boolean validateNode(Node<T> node) {
- boolean bst = super.validateNode(node);
- if (!bst)
- return false;
- AVLNode<T> avlNode = (AVLNode<T>) node;
- int balanceFactor = avlNode.getBalanceFactor();
- if (balanceFactor > 1 || balanceFactor < -1) {
- return false;
- }
- if (avlNode.isLeaf()) {
- if (avlNode.height != 1)
- return false;
- } else {
- AVLNode<T> avlNodeLesser = (AVLNode<T>) avlNode.lesser;
- int lesserHeight = 1;
- if (avlNodeLesser != null)
- lesserHeight = avlNodeLesser.height;
- AVLNode<T> avlNodeGreater = (AVLNode<T>) avlNode.greater;
- int greaterHeight = 1;
- if (avlNodeGreater != null)
- greaterHeight = avlNodeGreater.height;
- if (avlNode.height == (lesserHeight + 1) || avlNode.height == (greaterHeight + 1))
- return true;
- return false;
- }
- return true;
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return AVLTreePrinter.getString(this);
- }
- protected static class AVLNode<T extends Comparable<T>> extends Node<T> {
- protected int height = 1;
- /**
- * Constructor for an AVL node
- *
- * @param parent
- * Parent of the node in the tree, can be NULL.
- * @param value
- * Value of the node in the tree.
- */
- protected AVLNode(Node<T> parent, T value) {
- super(parent, value);
- }
- /**
- * Determines is this node is a leaf (has no children).
- *
- * @return True if this node is a leaf.
- */
- protected boolean isLeaf() {
- return ((lesser == null) && (greater == null));
- }
- /**
- * Updates the height of this node based on it's children.
- */
- protected int updateHeight() {
- int lesserHeight = 0;
- if (lesser != null) {
- AVLNode<T> lesserAVLNode = (AVLNode<T>) lesser;
- lesserHeight = lesserAVLNode.height;
- }
- int greaterHeight = 0;
- if (greater != null) {
- AVLNode<T> greaterAVLNode = (AVLNode<T>) greater;
- greaterHeight = greaterAVLNode.height;
- }
- if (lesserHeight > greaterHeight) {
- height = lesserHeight + 1;
- } else {
- height = greaterHeight + 1;
- }
- return height;
- }
- /**
- * Get the balance factor for this node.
- *
- * @return An integer representing the balance factor for this node. It
- * will be negative if the lesser branch is longer than the
- * greater branch.
- */
- protected int getBalanceFactor() {
- int lesserHeight = 0;
- if (lesser != null) {
- AVLNode<T> lesserAVLNode = (AVLNode<T>) lesser;
- lesserHeight = lesserAVLNode.height;
- }
- int greaterHeight = 0;
- if (greater != null) {
- AVLNode<T> greaterAVLNode = (AVLNode<T>) greater;
- greaterHeight = greaterAVLNode.height;
- }
- return greaterHeight - lesserHeight;
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return "value=" + id + " height=" + height + " parent=" + ((parent != null) ? parent.id : "NULL")
- + " lesser=" + ((lesser != null) ? lesser.id : "NULL") + " greater="
- + ((greater != null) ? greater.id : "NULL");
- }
- }
- protected static class AVLTreePrinter {
- public static <T extends Comparable<T>> String getString(AVLTree<T> tree) {
- if (tree.root == null)
- return "Tree has no nodes.";
- return getString((AVLNode<T>) tree.root, "", true);
- }
- public static <T extends Comparable<T>> String getString(AVLNode<T> node) {
- if (node == null)
- return "Sub-tree has no nodes.";
- return getString(node, "", true);
- }
- private static <T extends Comparable<T>> String getString(AVLNode<T> node, String prefix, boolean isTail) {
- StringBuilder builder = new StringBuilder();
- builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + node.height + ") " + node.id + "\n");
- List<Node<T>> children = null;
- if (node.lesser != null || node.greater != null) {
- children = new ArrayList<Node<T>>(2);
- if (node.lesser != null)
- children.add(node.lesser);
- if (node.greater != null)
- children.add(node.greater);
- }
- if (children != null) {
- for (int i = 0; i < children.size() - 1; i++) {
- builder.append(getString((AVLNode<T>) children.get(i), prefix + (isTail ? " " : "│ "), false));
- }
- if (children.size() >= 1) {
- builder.append(getString((AVLNode<T>) children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true));
- }
- }
- return builder.toString();
- }
- }
- }
|