blob: 53cb4b09f7fcea5b1d6298803a85c8d7788de756 [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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mycila.junit.concurrent.Concurrency;
import com.mycila.junit.concurrent.ConcurrentJunitRunner;
/**
* An AVL tree testcase.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
@RunWith(ConcurrentJunitRunner.class)
@Concurrency()
public class AvlTreeTest
{
private static final Logger LOG = LoggerFactory.getLogger( AvlTreeTest.class );
private AvlTree<Integer> createTree()
{
return new AvlTreeImpl<Integer>( new Comparator<Integer>()
{
public int compare( Integer i1, Integer i2 )
{
return i1.compareTo( i2 );
}
} );
}
@Test
public void testEmpty()
{
AvlTree<Integer> tree = createTree();
assertTrue( tree.isEmpty() );
assertNull( tree.getFirst() );
assertNull( tree.getLast() );
tree.remove( 97 ); // remove a non-existing key
assertTrue( tree.isEmpty() );
if ( LOG.isDebugEnabled() )
{
tree.printTree();
}
}
@Test
public void testFirstAndLast()
{
AvlTree<Integer> tree = createTree();
tree.insert( 7 );
assertFalse( tree.isEmpty() );
assertNotNull( tree.getFirst() );
assertNotNull( tree.getLast() );
tree.insert( 10 );
assertEquals( 2, tree.getSize() );
assertNotNull( tree.getFirst() );
assertNotNull( tree.getLast() );
assertFalse( tree.getFirst().equals( tree.getLast() ) );
assertTrue( tree.getFirst().getKey().equals( 7 ) );
assertTrue( tree.getLast().getKey().equals( 10 ) );
tree.insert( 3 );
assertTrue( tree.getFirst().getKey().equals( 3 ) );
assertTrue( tree.getLast().getKey().equals( 10 ) );
tree.insert( 11 );
assertTrue( tree.getFirst().getKey().equals( 3 ) );
assertTrue( tree.getLast().getKey().equals( 11 ) );
}
@Test
public void testInsert()
{
AvlTree<Integer> tree = createTree();
assertNull( tree.insert( 3 ) );
assertFalse( tree.isEmpty() );
assertTrue( 3 == tree.insert( 3 ) );// should be ignored
assertTrue( 1 == tree.getSize() );
assertNotNull( tree.getFirst() );
assertNotNull( tree.getLast() );
assertTrue( tree.getFirst() == tree.getLast() );
tree.remove( 3 );
tree.insert( 37 );
tree.insert( 70 );
tree.insert( 12 );
assertTrue( 3 == tree.getSize() );
tree.insert( 90 );
tree.insert( 25 );
tree.insert( 99 );
tree.insert( 91 );
tree.insert( 24 );
tree.insert( 28 );
tree.insert( 26 );
if ( LOG.isDebugEnabled() )
{
tree.printTree();
}
tree.remove( 24 ); // this causes a single left rotation on node with key 12
if ( LOG.isDebugEnabled() )
{
tree.printTree();
}
assertTrue( tree.getRoot().getLeft().key == 26 );
}
@Test
public void testSingleRightRotation()
{
AvlTree<Integer> tree = createTree();
// right rotation
tree.insert( 3 );
tree.insert( 2 );
tree.insert( 1 );
assertEquals( "1,2,3", getInorderForm( tree ) );
}
@Test
public void testSingleLeftRotation()
{
AvlTree<Integer> tree = createTree();
// left rotation
tree.insert( 1 );
tree.insert( 2 );
tree.insert( 3 );
assertEquals( "1,2,3", getInorderForm( tree ) );
}
@Test
public void testDoubleLeftRotation() // left-right totation
{
AvlTree<Integer> tree = createTree();
// double left rotation
tree.insert( 1 );
tree.insert( 3 );
tree.insert( 2 );
assertEquals( "1,2,3", getInorderForm( tree ) );
}
@Test
public void testDoubleRightRotation() // right-left totation
{
AvlTree<Integer> tree = createTree();
// double left rotation
tree.insert( 3 );
tree.insert( 1 );
tree.insert( 2 );
assertEquals( "1,2,3", getInorderForm( tree ) );
}
@Test
public void testLinks()
{
AvlTree<Integer> tree = createTree();
tree.insert( 37 );
tree.insert( 1 );
tree.insert( 2 );
tree.insert( 10 );
tree.insert( 11 );
tree.insert( 25 );
tree.insert( 5 );
assertTrue( 1 == tree.getFirst().key );
assertTrue( 37 == tree.getLast().key );
}
@Test
public void testRemove()
{
AvlTree<Integer> tree = createTree();
tree.insert( 3 );
tree.insert( 2 );
tree.insert( 1 );
LOG.debug( getLinkedText( tree ) );
tree.remove( 2 );
LOG.debug( getLinkedText( tree ) );
assertEquals( "1,3", getInorderForm( tree ) );
tree.remove( 1 );
assertEquals( "3", getInorderForm( tree ) );
assertTrue( 3 == tree.getRoot().key );
assertNull( tree.remove( 777 ) );// key not present
assertTrue( 3 == tree.remove( 3 ) );
assertTrue( tree.isEmpty() );
tree.insert( 37 );
tree.insert( 39 );
tree.insert( 27 );
tree.insert( 38 );
tree.insert( 21 );
tree.insert( 26 );
tree.insert( 43 );
assertEquals( "21,26,27,37,38,39,43", getInorderForm( tree ) );
tree.remove( 26 ); // remove a non-root non-leaf node in the left sub tree of root
assertEquals( "21,27,37,38,39,43", getInorderForm( tree ) );
tree.remove( 43 );
assertEquals( "21,27,37,38,39", getInorderForm( tree ) );
tree.remove( 39 );
assertEquals( "21,27,37,38", getInorderForm( tree ) );
assertTrue( 37 == tree.getRoot().key ); // check the root value
tree.remove( 38 ); // a double right rotation has to happen after this
assertTrue( 27 == tree.getRoot().key ); // check the root value after double right rotation
assertEquals( "21,27,37", getInorderForm( tree ) );
if ( LOG.isDebugEnabled() )
{
tree.printTree();
}
}
@Test
public void testLinkedNodes()
{
AvlTree<Integer> tree = createTree();
for ( int i = 0; i < 3; i++ )
{
tree.insert( i );
}
assertEquals( "[0]-->[1]-->[2]-->NULL", getLinkedText( tree ) );
tree.remove( 1 );
assertEquals( "[0]-->[2]-->NULL", getLinkedText( tree ) );
tree.insert( 4 );
tree.insert( 3 );
assertEquals( "[0]-->[2]-->[3]-->[4]-->NULL", getLinkedText( tree ) );
tree.remove( 0 );
assertEquals( "[2]-->[3]-->[4]-->NULL", getLinkedText( tree ) );
tree.remove( 3 );
assertEquals( "[2]-->[4]-->NULL", getLinkedText( tree ) );
}
@Test
public void testFind()
{
AvlTree<Integer> tree = createTree();
tree.insert( 1 );
tree.insert( 70 );
tree.insert( 21 );
tree.insert( 12 );
tree.insert( 27 );
tree.insert( 11 );
assertNotNull( tree.find( 11 ) );
assertNull( tree.find( 0 ) );
( ( AvlTreeImpl<Integer> ) tree ).setRoot( null );
assertNull( tree.find( 3 ) );
}
@Test
public void testFindGreater()
{
AvlTree<Integer> tree = createTree();
assertNull( tree.findGreater( 1 ) );
tree.insert( 1 );
assertNull( tree.findGreater( 1 ) );
tree.insert( 0 );
tree.insert( 3 );
tree.insert( 7 );
tree.insert( 6 );
tree.insert( 2 );
tree.insert( 8 );
assertFalse( 1 == tree.findGreater( 1 ).key );
assertTrue( 2 == tree.findGreater( 1 ).key );
assertTrue( 6 == tree.findGreater( 4 ).key );
assertNull( tree.findGreater( 8 ) );
}
@Test
public void testFindLess()
{
AvlTree<Integer> tree = createTree();
assertNull( tree.findLess( 1 ) );
tree.insert( 1 );
assertNull( tree.findLess( 1 ) );
tree.insert( 2 );
tree.insert( 7 );
tree.insert( 3 );
tree.insert( 6 );
tree.insert( 0 );
tree.insert( 37 );
assertFalse( 0 == tree.findLess( 5 ).key );
assertTrue( 3 == tree.findLess( 5 ).key );
assertTrue( 7 == tree.findLess( 8 ).key );
assertNull( tree.findLess( 0 ) );
}
@Test
public void testFindMaxMin()
{
AvlTree<Integer> tree = createTree();
tree.insert( 72 );
tree.insert( 79 );
tree.remove( 72 );// should call findMax internally
assertTrue( 79 == tree.getRoot().key );
tree.insert( 11 );
tree.remove( 79 ); // should call findMin internally
assertTrue( 11 == tree.getRoot().key );
}
@Test
public void testGetKeys()
{
AvlTree<Integer> tree = createTree();
tree.insert( 72 );
tree.insert( 79 );
tree.insert( 1 );
tree.insert( 2 );
tree.insert( 3 );
tree.insert( 7 );
tree.insert( 34 );
assertTrue( 7 == tree.getKeys().size() );
}
@Test
public void testTreeRoationAtLeftChildAfterDeletingRoot()
{
AvlTree<Integer> tree = createTree();
int[] keys =
{ 86, 110, 122, 2, 134, 26, 14, 182 }; // order is important to produce the expected tree
int[] expectedKeys =
{ 2, 14, 26, 86, 122, 134, 182 };
for ( int key : keys )
{
tree.insert( key );
}
tree.remove( 110 );
for ( int key : expectedKeys )
{
assertNotNull( "Should find " + key, tree.find( key ) );
}
}
@Test
public void testDetachNodesAtLeftChildAfterDeletingRoot()
{
AvlTree<Integer> tree = createTree();
int[] keys =
{ 110, 122, 2, 134, 86, 14, 26, 182 }; // order is important to produce the expected tree
for ( int key : keys )
{
tree.insert( key );
}
tree.remove( 110 );
assertEquals( 26, ( int ) tree.find( 14 ).right.key );
}
@Test
public void testRemoveInRightSubtree()
{
AvlTree<Integer> tree = createTree();
int[] keys =
{ 8, 4, 13, 6, 15, 7, 10, 5, 14, 2, 11, 3, 9, 1 }; // order is important to produce the expected tree
for ( int key : keys )
{
tree.insert( key );
}
tree.remove( 13 );
assertEquals( 11, ( int ) tree.find( 8 ).right.key );
}
@Test
public void testRemoveInLeftSubtree()
{
AvlTree<Integer> tree = createTree();
int[] keys =
{ 8, 4, 12, 6, 7, 16, 10, 5, 11, 9, 17, 5, 14, 2, 13, 1, 3 }; // order is important to produce the expected tree
for ( int key : keys )
{
tree.insert( key );
}
tree.remove( 16 );
assertEquals( 8, ( int ) tree.getRoot().key );
assertEquals( 12, ( int ) tree.getRoot().right.key );
assertEquals( 14, ( int ) tree.getRoot().right.right.key );
assertEquals( 13, ( int ) tree.find( 14 ).left.key );
}
private String getLinkedText( AvlTree<Integer> tree )
{
LinkedAvlNode<Integer> first = tree.getFirst();
StringBuilder sb = new StringBuilder();
while ( first != null )
{
sb.append( first )
.append( "-->" );
first = first.next;
}
sb.append( "NULL" );
return sb.toString();
}
private String getInorderForm( AvlTree<Integer> tree )
{
StringBuilder sb = new StringBuilder();
List<LinkedAvlNode<Integer>> path = new ArrayList<LinkedAvlNode<Integer>>();
traverse( tree.getRoot(), path );
int i;
for ( i = 0; i < path.size() - 1; i++ )
{
sb.append( path.get( i ).key )
.append( ',' );
}
sb.append( path.get( i ).key );
return sb.toString();
}
private void traverse( LinkedAvlNode<Integer> startNode, List<LinkedAvlNode<Integer>> path )
{
//1. pre-order
if ( startNode.left != null )
{
traverse( startNode.left, path );
}
path.add( startNode ); //2. in-order NOTE: change this line's position to change the type of traversal
if ( startNode.right != null )
{
traverse( startNode.right, path );
}
//3. post-order
}
@Test
public void testRemoveEmptyTree()
{
AvlTree<Integer> tree = createTree();
tree.remove( null );
assertEquals( 0, tree.getSize() );
tree.remove( 1 );
assertEquals( 0, tree.getSize() );
}
@Test
public void testRemoveOneNode()
{
AvlTree<Integer> tree = createTree();
tree.insert( 1 );
assertEquals( 1, tree.getSize() );
tree.remove( 1 );
assertEquals( 0, tree.getSize() );
}
@Test
public void testRemoveOneNodeWithRight()
{
AvlTree<Integer> tree = createTree();
tree.insert( 1 );
tree.insert( 2 );
assertEquals( 2, tree.getSize() );
assertEquals( "1,2", getInorderForm( tree ) );
tree.remove( 1 );
assertEquals( 1, tree.getSize() );
assertEquals( Integer.valueOf( 2 ), tree.getRoot().getKey() );
}
}