AVLTree.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.commons.math3.geometry.partitioning.utilities;
- /** This class implements AVL trees.
- *
- * <p>The purpose of this class is to sort elements while allowing
- * duplicate elements (i.e. such that {@code a.equals(b)} is
- * true). The {@code SortedSet} interface does not allow this, so
- * a specific class is needed. Null elements are not allowed.</p>
- *
- * <p>Since the {@code equals} method is not sufficient to
- * differentiate elements, the {@link #delete delete} method is
- * implemented using the equality operator.</p>
- *
- * <p>In order to clearly mark the methods provided here do not have
- * the same semantics as the ones specified in the
- * {@code SortedSet} interface, different names are used
- * ({@code add} has been replaced by {@link #insert insert} and
- * {@code remove} has been replaced by {@link #delete
- * delete}).</p>
- *
- * <p>This class is based on the C implementation Georg Kraml has put
- * in the public domain. Unfortunately, his <a
- * href="www.purists.org/georg/avltree/index.html">page</a> seems not
- * to exist any more.</p>
- *
- * @param <T> the type of the elements
- *
- * @since 3.0
- * @deprecated as of 3.4, this class is not used anymore and considered
- * to be out of scope of Apache Commons Math
- */
- @Deprecated
- public class AVLTree<T extends Comparable<T>> {
- /** Top level node. */
- private Node top;
- /** Build an empty tree.
- */
- public AVLTree() {
- top = null;
- }
- /** Insert an element in the tree.
- * @param element element to insert (silently ignored if null)
- */
- public void insert(final T element) {
- if (element != null) {
- if (top == null) {
- top = new Node(element, null);
- } else {
- top.insert(element);
- }
- }
- }
- /** Delete an element from the tree.
- * <p>The element is deleted only if there is a node {@code n}
- * containing exactly the element instance specified, i.e. for which
- * {@code n.getElement() == element}. This is purposely
- * <em>different</em> from the specification of the
- * {@code java.util.Set} {@code remove} method (in fact,
- * this is the reason why a specific class has been developed).</p>
- * @param element element to delete (silently ignored if null)
- * @return true if the element was deleted from the tree
- */
- public boolean delete(final T element) {
- if (element != null) {
- for (Node node = getNotSmaller(element); node != null; node = node.getNext()) {
- // loop over all elements neither smaller nor larger
- // than the specified one
- if (node.element == element) {
- node.delete();
- return true;
- } else if (node.element.compareTo(element) > 0) {
- // all the remaining elements are known to be larger,
- // the element is not in the tree
- return false;
- }
- }
- }
- return false;
- }
- /** Check if the tree is empty.
- * @return true if the tree is empty
- */
- public boolean isEmpty() {
- return top == null;
- }
- /** Get the number of elements of the tree.
- * @return number of elements contained in the tree
- */
- public int size() {
- return (top == null) ? 0 : top.size();
- }
- /** Get the node whose element is the smallest one in the tree.
- * @return the tree node containing the smallest element in the tree
- * or null if the tree is empty
- * @see #getLargest
- * @see #getNotSmaller
- * @see #getNotLarger
- * @see Node#getPrevious
- * @see Node#getNext
- */
- public Node getSmallest() {
- return (top == null) ? null : top.getSmallest();
- }
- /** Get the node whose element is the largest one in the tree.
- * @return the tree node containing the largest element in the tree
- * or null if the tree is empty
- * @see #getSmallest
- * @see #getNotSmaller
- * @see #getNotLarger
- * @see Node#getPrevious
- * @see Node#getNext
- */
- public Node getLargest() {
- return (top == null) ? null : top.getLargest();
- }
- /** Get the node whose element is not smaller than the reference object.
- * @param reference reference object (may not be in the tree)
- * @return the tree node containing the smallest element not smaller
- * than the reference object or null if either the tree is empty or
- * all its elements are smaller than the reference object
- * @see #getSmallest
- * @see #getLargest
- * @see #getNotLarger
- * @see Node#getPrevious
- * @see Node#getNext
- */
- public Node getNotSmaller(final T reference) {
- Node candidate = null;
- for (Node node = top; node != null;) {
- if (node.element.compareTo(reference) < 0) {
- if (node.right == null) {
- return candidate;
- }
- node = node.right;
- } else {
- candidate = node;
- if (node.left == null) {
- return candidate;
- }
- node = node.left;
- }
- }
- return null;
- }
- /** Get the node whose element is not larger than the reference object.
- * @param reference reference object (may not be in the tree)
- * @return the tree node containing the largest element not larger
- * than the reference object (in which case the node is guaranteed
- * not to be empty) or null if either the tree is empty or all its
- * elements are larger than the reference object
- * @see #getSmallest
- * @see #getLargest
- * @see #getNotSmaller
- * @see Node#getPrevious
- * @see Node#getNext
- */
- public Node getNotLarger(final T reference) {
- Node candidate = null;
- for (Node node = top; node != null;) {
- if (node.element.compareTo(reference) > 0) {
- if (node.left == null) {
- return candidate;
- }
- node = node.left;
- } else {
- candidate = node;
- if (node.right == null) {
- return candidate;
- }
- node = node.right;
- }
- }
- return null;
- }
- /** Enum for tree skew factor. */
- private static enum Skew {
- /** Code for left high trees. */
- LEFT_HIGH,
- /** Code for right high trees. */
- RIGHT_HIGH,
- /** Code for Skew.BALANCED trees. */
- BALANCED;
- }
- /** This class implements AVL trees nodes.
- * <p>AVL tree nodes implement all the logical structure of the
- * tree. Nodes are created by the {@link AVLTree AVLTree} class.</p>
- * <p>The nodes are not independant from each other but must obey
- * specific balancing constraints and the tree structure is
- * rearranged as elements are inserted or deleted from the tree. The
- * creation, modification and tree-related navigation methods have
- * therefore restricted access. Only the order-related navigation,
- * reading and delete methods are public.</p>
- * @see AVLTree
- */
- public class Node {
- /** Element contained in the current node. */
- private T element;
- /** Left sub-tree. */
- private Node left;
- /** Right sub-tree. */
- private Node right;
- /** Parent tree. */
- private Node parent;
- /** Skew factor. */
- private Skew skew;
- /** Build a node for a specified element.
- * @param element element
- * @param parent parent node
- */
- Node(final T element, final Node parent) {
- this.element = element;
- left = null;
- right = null;
- this.parent = parent;
- skew = Skew.BALANCED;
- }
- /** Get the contained element.
- * @return element contained in the node
- */
- public T getElement() {
- return element;
- }
- /** Get the number of elements of the tree rooted at this node.
- * @return number of elements contained in the tree rooted at this node
- */
- int size() {
- return 1 + ((left == null) ? 0 : left.size()) + ((right == null) ? 0 : right.size());
- }
- /** Get the node whose element is the smallest one in the tree
- * rooted at this node.
- * @return the tree node containing the smallest element in the
- * tree rooted at this node or null if the tree is empty
- * @see #getLargest
- */
- Node getSmallest() {
- Node node = this;
- while (node.left != null) {
- node = node.left;
- }
- return node;
- }
- /** Get the node whose element is the largest one in the tree
- * rooted at this node.
- * @return the tree node containing the largest element in the
- * tree rooted at this node or null if the tree is empty
- * @see #getSmallest
- */
- Node getLargest() {
- Node node = this;
- while (node.right != null) {
- node = node.right;
- }
- return node;
- }
- /** Get the node containing the next smaller or equal element.
- * @return node containing the next smaller or equal element or
- * null if there is no smaller or equal element in the tree
- * @see #getNext
- */
- public Node getPrevious() {
- if (left != null) {
- final Node node = left.getLargest();
- if (node != null) {
- return node;
- }
- }
- for (Node node = this; node.parent != null; node = node.parent) {
- if (node != node.parent.left) {
- return node.parent;
- }
- }
- return null;
- }
- /** Get the node containing the next larger or equal element.
- * @return node containing the next larger or equal element (in
- * which case the node is guaranteed not to be empty) or null if
- * there is no larger or equal element in the tree
- * @see #getPrevious
- */
- public Node getNext() {
- if (right != null) {
- final Node node = right.getSmallest();
- if (node != null) {
- return node;
- }
- }
- for (Node node = this; node.parent != null; node = node.parent) {
- if (node != node.parent.right) {
- return node.parent;
- }
- }
- return null;
- }
- /** Insert an element in a sub-tree.
- * @param newElement element to insert
- * @return true if the parent tree should be re-Skew.BALANCED
- */
- boolean insert(final T newElement) {
- if (newElement.compareTo(this.element) < 0) {
- // the inserted element is smaller than the node
- if (left == null) {
- left = new Node(newElement, this);
- return rebalanceLeftGrown();
- }
- return left.insert(newElement) ? rebalanceLeftGrown() : false;
- }
- // the inserted element is equal to or greater than the node
- if (right == null) {
- right = new Node(newElement, this);
- return rebalanceRightGrown();
- }
- return right.insert(newElement) ? rebalanceRightGrown() : false;
- }
- /** Delete the node from the tree.
- */
- public void delete() {
- if ((parent == null) && (left == null) && (right == null)) {
- // this was the last node, the tree is now empty
- element = null;
- top = null;
- } else {
- Node node;
- Node child;
- boolean leftShrunk;
- if ((left == null) && (right == null)) {
- node = this;
- element = null;
- leftShrunk = node == node.parent.left;
- child = null;
- } else {
- node = (left != null) ? left.getLargest() : right.getSmallest();
- element = node.element;
- leftShrunk = node == node.parent.left;
- child = (node.left != null) ? node.left : node.right;
- }
- node = node.parent;
- if (leftShrunk) {
- node.left = child;
- } else {
- node.right = child;
- }
- if (child != null) {
- child.parent = node;
- }
- while (leftShrunk ? node.rebalanceLeftShrunk() : node.rebalanceRightShrunk()) {
- if (node.parent == null) {
- return;
- }
- leftShrunk = node == node.parent.left;
- node = node.parent;
- }
- }
- }
- /** Re-balance the instance as left sub-tree has grown.
- * @return true if the parent tree should be reSkew.BALANCED too
- */
- private boolean rebalanceLeftGrown() {
- switch (skew) {
- case LEFT_HIGH:
- if (left.skew == Skew.LEFT_HIGH) {
- rotateCW();
- skew = Skew.BALANCED;
- right.skew = Skew.BALANCED;
- } else {
- final Skew s = left.right.skew;
- left.rotateCCW();
- rotateCW();
- switch(s) {
- case LEFT_HIGH:
- left.skew = Skew.BALANCED;
- right.skew = Skew.RIGHT_HIGH;
- break;
- case RIGHT_HIGH:
- left.skew = Skew.LEFT_HIGH;
- right.skew = Skew.BALANCED;
- break;
- default:
- left.skew = Skew.BALANCED;
- right.skew = Skew.BALANCED;
- }
- skew = Skew.BALANCED;
- }
- return false;
- case RIGHT_HIGH:
- skew = Skew.BALANCED;
- return false;
- default:
- skew = Skew.LEFT_HIGH;
- return true;
- }
- }
- /** Re-balance the instance as right sub-tree has grown.
- * @return true if the parent tree should be reSkew.BALANCED too
- */
- private boolean rebalanceRightGrown() {
- switch (skew) {
- case LEFT_HIGH:
- skew = Skew.BALANCED;
- return false;
- case RIGHT_HIGH:
- if (right.skew == Skew.RIGHT_HIGH) {
- rotateCCW();
- skew = Skew.BALANCED;
- left.skew = Skew.BALANCED;
- } else {
- final Skew s = right.left.skew;
- right.rotateCW();
- rotateCCW();
- switch (s) {
- case LEFT_HIGH:
- left.skew = Skew.BALANCED;
- right.skew = Skew.RIGHT_HIGH;
- break;
- case RIGHT_HIGH:
- left.skew = Skew.LEFT_HIGH;
- right.skew = Skew.BALANCED;
- break;
- default:
- left.skew = Skew.BALANCED;
- right.skew = Skew.BALANCED;
- }
- skew = Skew.BALANCED;
- }
- return false;
- default:
- skew = Skew.RIGHT_HIGH;
- return true;
- }
- }
- /** Re-balance the instance as left sub-tree has shrunk.
- * @return true if the parent tree should be reSkew.BALANCED too
- */
- private boolean rebalanceLeftShrunk() {
- switch (skew) {
- case LEFT_HIGH:
- skew = Skew.BALANCED;
- return true;
- case RIGHT_HIGH:
- if (right.skew == Skew.RIGHT_HIGH) {
- rotateCCW();
- skew = Skew.BALANCED;
- left.skew = Skew.BALANCED;
- return true;
- } else if (right.skew == Skew.BALANCED) {
- rotateCCW();
- skew = Skew.LEFT_HIGH;
- left.skew = Skew.RIGHT_HIGH;
- return false;
- } else {
- final Skew s = right.left.skew;
- right.rotateCW();
- rotateCCW();
- switch (s) {
- case LEFT_HIGH:
- left.skew = Skew.BALANCED;
- right.skew = Skew.RIGHT_HIGH;
- break;
- case RIGHT_HIGH:
- left.skew = Skew.LEFT_HIGH;
- right.skew = Skew.BALANCED;
- break;
- default:
- left.skew = Skew.BALANCED;
- right.skew = Skew.BALANCED;
- }
- skew = Skew.BALANCED;
- return true;
- }
- default:
- skew = Skew.RIGHT_HIGH;
- return false;
- }
- }
- /** Re-balance the instance as right sub-tree has shrunk.
- * @return true if the parent tree should be reSkew.BALANCED too
- */
- private boolean rebalanceRightShrunk() {
- switch (skew) {
- case RIGHT_HIGH:
- skew = Skew.BALANCED;
- return true;
- case LEFT_HIGH:
- if (left.skew == Skew.LEFT_HIGH) {
- rotateCW();
- skew = Skew.BALANCED;
- right.skew = Skew.BALANCED;
- return true;
- } else if (left.skew == Skew.BALANCED) {
- rotateCW();
- skew = Skew.RIGHT_HIGH;
- right.skew = Skew.LEFT_HIGH;
- return false;
- } else {
- final Skew s = left.right.skew;
- left.rotateCCW();
- rotateCW();
- switch (s) {
- case LEFT_HIGH:
- left.skew = Skew.BALANCED;
- right.skew = Skew.RIGHT_HIGH;
- break;
- case RIGHT_HIGH:
- left.skew = Skew.LEFT_HIGH;
- right.skew = Skew.BALANCED;
- break;
- default:
- left.skew = Skew.BALANCED;
- right.skew = Skew.BALANCED;
- }
- skew = Skew.BALANCED;
- return true;
- }
- default:
- skew = Skew.LEFT_HIGH;
- return false;
- }
- }
- /** Perform a clockwise rotation rooted at the instance.
- * <p>The skew factor are not updated by this method, they
- * <em>must</em> be updated by the caller</p>
- */
- private void rotateCW() {
- final T tmpElt = element;
- element = left.element;
- left.element = tmpElt;
- final Node tmpNode = left;
- left = tmpNode.left;
- tmpNode.left = tmpNode.right;
- tmpNode.right = right;
- right = tmpNode;
- if (left != null) {
- left.parent = this;
- }
- if (right.right != null) {
- right.right.parent = right;
- }
- }
- /** Perform a counter-clockwise rotation rooted at the instance.
- * <p>The skew factor are not updated by this method, they
- * <em>must</em> be updated by the caller</p>
- */
- private void rotateCCW() {
- final T tmpElt = element;
- element = right.element;
- right.element = tmpElt;
- final Node tmpNode = right;
- right = tmpNode.right;
- tmpNode.right = tmpNode.left;
- tmpNode.left = left;
- left = tmpNode;
- if (right != null) {
- right.parent = this;
- }
- if (left.left != null) {
- left.left.parent = left;
- }
- }
- }
- }