summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java')
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java634
1 files changed, 634 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java
new file mode 100644
index 0000000..00c9d3e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java
@@ -0,0 +1,634 @@
+/*
+ * 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 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;
+ }
+
+ }
+
+ }
+
+}