blob: e08e45541cdc0484028f56974ca80736a45cd150 [file] [log] [blame]
/*
* 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.directory.server.core.avltree;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* An AVL tree implementation
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class AvlTreeImpl<K> implements AvlTree<K>
{
/** the root of the tree */
private LinkedAvlNode<K> root;
/** The Comparator used for comparing the keys */
private Comparator<K> comparator;
/** node representing the start of the doubly linked list formed with the tree nodes */
private LinkedAvlNode<K> first;
/** node representing the end of the doubly linked list formed with the tree nodes */
private LinkedAvlNode<K> last;
/** size of the tree */
private int size;
/**
* Creates a new instance of AVLTree.
*
* @param comparator the comparator to be used for comparing keys
*/
public AvlTreeImpl( Comparator<K> comparator)
{
this.comparator = comparator;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#getComparator()
*/
public Comparator<K> getComparator()
{
return comparator;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#insert(K)
*/
public K insert( K key )
{
LinkedAvlNode<K> node, temp;
LinkedAvlNode<K> parent = null;
int c;
if ( root == null )
{
root = new LinkedAvlNode<K>( key );
first = root;
last = root;
size++;
return null;
}
node = new LinkedAvlNode<K>( key );
temp = root;
List<LinkedAvlNode<K>> treePath = new ArrayList<LinkedAvlNode<K>>();
while( temp != null )
{
treePath.add(0, temp ); // last node first, for the sake of balance factor computation
parent = temp;
c = comparator.compare( key, temp.getKey() );
if( c == 0 )
{
return key; // key already exists
}
if( c < 0 )
{
temp.isLeft = true;
temp = temp.getLeft();
}
else
{
temp.isLeft = false;
temp = temp.getRight();
}
}
if( ( c = comparator.compare( key, parent.getKey() ) ) < 0 )
{
parent.setLeft( node );
}
else
{
parent.setRight( node );
}
insertInList( node, parent, c );
treePath.add( 0, node );
balance(treePath);
size++;
return null;
}
private void removeFromList(LinkedAvlNode<K> node)
{
if( node.next == null && node.previous == null ) // should happen in case of tree having single node
{
first = last = null;
}
else if( node.next == null ) // last node
{
node.previous.next = null;
last = node.previous;
}
else if( node.previous == null ) // first node
{
node.next.previous = null;
first = node.next;
}
else // somewhere in middle
{
node.previous.next = node.next;
node.next.previous = node.previous;
}
}
private void insertInList(LinkedAvlNode<K> node, LinkedAvlNode<K> parentNode, int pos)
{
if( pos < 0 )
{
if( last == null )
{
last = parentNode;
}
if( parentNode.previous == null )
{
first = node;
}
else
{
parentNode.previous.next = node ;
node.previous = parentNode.previous;
}
node.next = parentNode;
parentNode.previous = node;
}
else if( pos > 0 )
{
if( parentNode.next == null )
{
last = node;
}
else
{
parentNode.next.previous = node;
node.next = parentNode.next;
}
node.previous = parentNode;
parentNode.next = node;
}
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#remove(K)
*/
public K remove( K key )
{
LinkedAvlNode<K> temp = null;
LinkedAvlNode<K> y = null;
List<LinkedAvlNode<K>> treePath = new ArrayList<LinkedAvlNode<K>>();
treePath = find( key, root, treePath);
if( treePath == null )
{
return null;
}
temp = treePath.remove( 0 );
// remove from the doubly linked
removeFromList(temp);
if( temp.isLeaf() )
{
if( temp == root )
{
root = null;
size--;
return key;
}
if( !treePath.isEmpty() )
{
detachNodes( temp, treePath.get( 0 ) );
}
}
else
{
if( temp.left != null )
{
List<LinkedAvlNode<K>> leftTreePath = findMax( temp.left );
y = leftTreePath.remove( 0 );
if( leftTreePath.isEmpty() ) // y is the left child of root and y is a leaf
{
detachNodes( y, temp );
}
else
{
detachNodes( y, leftTreePath.remove( 0 ) );
}
leftTreePath.addAll( treePath );
treePath = leftTreePath;
y.right = temp.right; // assign the right here left will be assigned in replaceNode()
if( temp == root )
{
y.left = temp.left;
root = y;
}
else
{
replaceNode( temp, y, treePath.get( 0 ) );
}
}
else if( temp.right != null )
{
List<LinkedAvlNode<K>> rightTreePath = findMin( temp.right );
y = rightTreePath.remove( 0 );
if( rightTreePath.isEmpty() )
{
detachNodes( y, temp ); // y is the right child of root and y is a leaf
}
else
{
detachNodes( y, rightTreePath.remove( 0 ) );
}
rightTreePath.addAll( treePath );
treePath = rightTreePath;
y.right = temp.right; // assign the right here left will be assigned in replaceNode()
if( temp == root )
{
y.right = temp.right;
root = y;
}
else
{
replaceNode( temp, y, treePath.get( 0 ) );
}
}
}
treePath.add( 0, y ); // y can be null but getBalance returns 0 so np
balance( treePath );
size--;
return key;
}
/**
* Balances the tree by visiting the nodes present in the List of nodes present in the
* treePath parameter.<br><br>
*
* This really does the balancing if the height of the tree is greater than 2 and the<br>
* balance factor is greater than +1 or less than -1.<br><br>
* For an excellent info please read the
* <a href="http://en.wikipedia.org/wiki/Avl_tree">Wikipedia article on AVL tree</a>.
*
* @param treePath the traversed list of LinkedAvlNodes after performing an insert/delete operation.
*/
private void balance( List<LinkedAvlNode<K>> treePath )
{
LinkedAvlNode<K> parentNode = null;
int size = treePath.size();
for( LinkedAvlNode<K> node: treePath )
{
int balFactor = getBalance( node );
if( node != root && treePath.indexOf( node ) < ( size - 1 ) )
{
parentNode = treePath.get( treePath.indexOf( node ) + 1 );
}
if( balFactor > 1 )
{
if( getBalance( node.right ) <= -1)
{
//------rotate double-left--------
rotateSingleRight( node.right, node );
rotateSingleLeft( node, parentNode );
}
else // rotate single-left
{
rotateSingleLeft( node, parentNode );
}
}
else if( balFactor < -1 )
{
if( getBalance( node.left ) >= 1)
{
//------rotate double-right--------
rotateSingleLeft( node.left, node );
rotateSingleRight( node, parentNode );
}
else
{
rotateSingleRight( node, parentNode );
}
}
}
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#isEmpty()
*/
public boolean isEmpty()
{
return root == null;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#getSize()
*/
//NOTE: This method is internally used by AVLTreeMarshaller
public int getSize()
{
return size;
}
/**
* Set the size of the tree.
*
* Note : this method is used by the deserialization method
*
* @param size the size of the tree
*/
/* no protection */ void setSize( int size )
{
this.size = size;
}
/**
* Set the root of the tree.
*
* Note : this method is used by the deserialization method
*
* @param root the root of the tree
*/
/* no protection */ void setRoot( LinkedAvlNode<K> root )
{
this.root = root;
}
/**
* Set the first element of the tree
*
* Note : this method is used by the deserialization method
*
* @param first the first element to be added
*/
/* no protection */ void setFirst( LinkedAvlNode<K> first )
{
this.first = first;
size++;
}
/**
* Set the last element of the tree
*
* Note : this method is used by the deserialization method
*
* @param last the last element to be added
*/
/* no protection */ void setLast( LinkedAvlNode<K> last )
{
this.last = last;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#getRoot()
*/
public LinkedAvlNode<K> getRoot()
{
return root;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#getKeys()
*/
public List<K> getKeys()
{
List<K> keys = new ArrayList<K>();
LinkedAvlNode<K> node = first;
while( node != null )
{
keys.add( node.key );
node = node.next;
}
return keys;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#printTree()
*/
public void printTree()
{
if( isEmpty() )
{
System.out.println( "Tree is empty" );
return;
}
getRoot().setDepth( 0 );
System.out.println( getRoot() );
visit( getRoot().getRight(), getRoot() );
visit( getRoot().getLeft(), getRoot() );
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#getFirst()
*/
public LinkedAvlNode<K> getFirst()
{
return first;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#getLast()
*/
public LinkedAvlNode<K> getLast()
{
return last;
}
/**
* Rotate the node left side once.
*
* @param node the LinkedAvlNode to be rotated
* @param parentNode parent LinkedAvlNode of node
*/
private void rotateSingleLeft(LinkedAvlNode<K> node, LinkedAvlNode<K> parentNode)
{
LinkedAvlNode<K> temp;
//------rotate single-left--------
temp = node.right;
node.right = temp.left;
temp.left = node;
if( node == root )
{
root = temp;
}
else if( parentNode != null )
{
if( parentNode.left == node )
{
parentNode.left = temp;
}
else if( parentNode.right == node )
{
parentNode.right = temp;
}
}
}
/**
* Rotate the node right side once.
*
* @param node the LinkedAvlNode to be rotated
* @param parentNode parent LinkedAvlNode of node
*/
private void rotateSingleRight(LinkedAvlNode<K> node, LinkedAvlNode<K> parentNode)
{
LinkedAvlNode<K> temp;
//------rotate single-right--------
temp = node.left;
node.left = temp.right;
temp.right = node;
if( node == root )
{
root = temp;
}
else if( parentNode != null )
{
if( parentNode.left == node )
{
parentNode.left = temp;
}
else if( parentNode.right == node )
{
parentNode.right = temp;
}
}
/*
when the 'parentNode' param is null then the node under rotation is a child of ROOT.
Most likely this condition executes when the root node is deleted and balancing is required.
*/
else if( root != null && root.left == node )
{
root.left = temp;
// no need to check for right node
}
}
/**
* Detach a LinkedAvlNode from its parent
*
* @param node the LinkedAvlNode to be detached
* @param parentNode the parent LinkedAvlNode of the node
*/
private void detachNodes(LinkedAvlNode<K> node, LinkedAvlNode<K> parentNode)
{
if( parentNode != null )
{
if( node == parentNode.left )
{
parentNode.left = node.left;
}
else if( node == parentNode.right )
{
parentNode.right = node.left;
}
}
}
/**
*
* Replace a LinkedAvlNode to be removed with a new existing LinkedAvlNode
*
* @param deleteNode the LinkedAvlNode to be deleted
* @param replaceNode the LinkedAvlNode to replace the deleteNode
* @param parentNode the parent LinkedAvlNode of deleteNode
*/
private void replaceNode(LinkedAvlNode<K> deleteNode, LinkedAvlNode<K> replaceNode, LinkedAvlNode<K> parentNode)
{
if( parentNode != null )
{
replaceNode.left = deleteNode.left;
if( deleteNode == parentNode.left )
{
parentNode.left = replaceNode;
}
else if( deleteNode == parentNode.right )
{
parentNode.right = replaceNode;
}
}
}
/**
*
* Find a LinkedAvlNode with the given key value in the tree starting from the startNode.
*
* @param key the key to find
* @param startNode starting node of a subtree/tree
* @param path the list to be filled with traversed nodes
* @return the list of traversed LinkedAvlNodes.
*/
private List<LinkedAvlNode<K>> find( K key, LinkedAvlNode<K> startNode, List<LinkedAvlNode<K>> path )
{
int c;
if( startNode == null )
{
return null;
}
path.add( 0, startNode );
c = comparator.compare( key, startNode.key );
if( c == 0 )
{
return path;
}
else if( c > 0 )
{
return find( key, startNode.right, path );
}
else if( c < 0 )
{
return find( key, startNode.left, path );
}
return null;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#findGreater(K)
*/
public LinkedAvlNode<K> findGreater( K key )
{
LinkedAvlNode<K> result = fetchNonNullNode( key, root, root);
if( result == null )
{
return null;
}
else if( comparator.compare( key, result.key ) < 0 )
{
return result;
}
return result.next;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#findGreaterOrEqual(K)
*/
public LinkedAvlNode<K> findGreaterOrEqual( K key )
{
LinkedAvlNode<K> result = fetchNonNullNode( key, root, root);
if( result == null )
{
return null;
}
else if( comparator.compare( key, result.key ) <= 0 )
{
return result;
}
return result.next;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#findLess(K)
*/
public LinkedAvlNode<K> findLess( K key )
{
LinkedAvlNode<K> result = fetchNonNullNode( key, root, root);
if( result == null )
{
return null;
}
else if( comparator.compare( key, result.key ) > 0 )
{
return result;
}
return result.previous;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#findLessOrEqual(K)
*/
public LinkedAvlNode<K> findLessOrEqual( K key )
{
LinkedAvlNode<K> result = fetchNonNullNode( key, root, root);
if( result == null )
{
return null;
}
else if( comparator.compare( key, result.key ) >= 0 )
{
return result;
}
return result.previous;
}
/*
* This method returns the last visited non-null node in case if the node with the given key
* is not present. This method should not be used as general purpose lookup method.
* This is written to assist the findGreater, findLess methods.
*/
private LinkedAvlNode<K> fetchNonNullNode( K key, LinkedAvlNode<K> startNode, LinkedAvlNode<K> parent )
{
if( startNode == null )
{
return parent;
}
int c = comparator.compare( key, startNode.key );
parent = startNode;
if( c > 0 )
{
return fetchNonNullNode( key, startNode.right, parent );
}
else if( c < 0 )
{
return fetchNonNullNode( key, startNode.left, parent );
}
return startNode;
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.avltree.AvlTree#find(K)
*/
public LinkedAvlNode<K> find( K key )
{
return find( key, root);
}
private LinkedAvlNode<K> find( K key, LinkedAvlNode<K> startNode)
{
int c;
if( startNode == null )
{
return null;
}
c = comparator.compare( key, startNode.key );
if( c > 0 )
{
startNode.isLeft = false;
return find( key, startNode.right );
}
else if( c < 0 )
{
startNode.isLeft = true;
return find( key, startNode.left );
}
return startNode;
}
/**
* Find the LinkedAvlNode having the max key value in the tree starting from the startNode.
*
* @param startNode starting node of a subtree/tree
* @return the list of traversed LinkedAvlNodes.
*/
private List<LinkedAvlNode<K>> findMax( LinkedAvlNode<K> startNode )
{
LinkedAvlNode<K> x = startNode;
LinkedAvlNode<K> y = null;
List<LinkedAvlNode<K>> path;
if( x == null )
{
return null;
}
while( x.right != null )
{
x.isLeft = false;
y = x;
x = x.right;
}
path = new ArrayList<LinkedAvlNode<K>>(2);
path.add( x );
if ( y != null )
{
path.add( y );
}
return path;
}
/**
* Find the LinkedAvlNode having the min key value in the tree starting from the startNode.
*
* @param startNode starting node of a subtree/tree
* @return the list of traversed LinkedAvlNodes.
*/
private List<LinkedAvlNode<K>> findMin( LinkedAvlNode<K> startNode )
{
LinkedAvlNode<K> x = startNode;
LinkedAvlNode<K> y = null;
List<LinkedAvlNode<K>> path;
if( x == null )
{
return null;
}
while( x.left != null )
{
x.isLeft = true;
y = x;
x = x.left;
}
path = new ArrayList<LinkedAvlNode<K>>(2);
path.add( x );
if ( y != null )
{
path.add( y );
}
return path;
}
/**
* Get balance-factor of the given LinkedAvlNode.
*
* @param node a LinkedAvlNode
* @return balance-factor of the node
*/
private int getBalance( LinkedAvlNode<K> node )
{
if( node == null)
{
return 0;
}
return node.getBalance();
}
private void visit( LinkedAvlNode<K> node, LinkedAvlNode<K> parentNode )
{
if( node == null )
{
return;
}
if( !node.isLeaf() )
{
node.setDepth( parentNode.getDepth() + 1 );
}
for( int i=0; i < parentNode.getDepth(); i++ )
{
System.out.print( "| " );
}
String type = "";
if( node == parentNode.left )
{
type = "L";
}
else if( node == parentNode.right )
{
type = "R";
}
System.out.println( "|--" + node + type );
if ( node.getRight() != null )
{
visit( node.getRight(), node );
}
if( node.getLeft() != null )
{
visit( node.getLeft(), node );
}
}
}