简体   繁体   中英

Finding nth node in binary search tree

Hi all I am working on a class project using binary search trees. I am having trouble trying to find the nth node of the binary search tree. I understand the concept of using in order traversal and using a counter but I am having trouble putting it into code. If anyone could help it would be greatly appreciated. Sorry for the long code. The method in question is the nthElement(int n, BinaryNode<AnyType> t) method. I am not sure how to increment the counter.

package proj2;

// BinarySearchTree class
//
// CONSTRUCTION: with no initializer
//
// ******************PUBLIC OPERATIONS*********************
// void insert( x )       --> Insert x
// void remove( x )       --> Remove x
// boolean contains( x )  --> Return true if x is present
// Comparable findMin( )  --> Return smallest item
// Comparable findMax( )  --> Return largest item
// boolean isEmpty( )     --> Return true if empty; else false
// void makeEmpty( )      --> Remove all items
// void printTree( )      --> Print tree in sorted order
// ******************ERRORS********************************
// Throws UnderflowException as appropriate

/**
 * Implements an unbalanced binary search tree.
 * Note that all "matching" is based on the compareTo method.
 * @author Mark Allen Weiss
 */
public class BinarySearchTree<AnyType extends Comparable<? super AnyType>>
{
/** The tree root. */
private BinaryNode<AnyType> root;

/** The tree size. */
private int treeSize;

/**
 * Construct the tree.
 */
public BinarySearchTree( )
{
    root = null;
}

/**
 * Insert into the tree; duplicates are ignored.
 * @param x the item to insert.
 */
public void insert( AnyType x )
{
    root = insert( x, root );
}

/**
 * Remove from the tree. Nothing is done if x is not found.
 * @param x the item to remove.
 */
public void remove( AnyType x )
{
    root = remove( x, root );
}

/**
 * Find the smallest item in the tree.
 * @return smallest item or null if empty.
 */
public AnyType findMin( )
{
    if( isEmpty( ) )
        throw new UnderflowException( );
    return findMin( root ).element;
}

/**
 * Find the largest item in the tree.
 * @return the largest item of null if empty.
 */
public AnyType findMax( )
{
    if( isEmpty( ) )
        throw new UnderflowException( );
    return findMax( root ).element;
}

/**
 * Find an item in the tree.
 * @param x the item to search for.
 * @return true if not found.
 */
public boolean contains( AnyType x )
{
    return contains( x, root );
}

/**
 * Count the number of nodes in the tree.
 * @return the tree size.
 */
public int treeSize(){

    treeSize = treeSize(root);
    return treeSize;

}

/**
 * Make the tree logically empty.
 */
public void makeEmpty( )
{
    root = null;
}

/**
 * Test if the tree is logically empty.
 * @return true if empty, false otherwise.
 */
public boolean isEmpty( )
{
    return root == null;
}

/**
 * Print the tree contents in sorted order.
 */
public void printTree( )
{
    if( isEmpty( ) )
        System.out.println( "Empty tree" );
    else
        printTree( root );
}

public BinaryNode<AnyType> nthElement(int n){

    return nthElement(n, root);

}

/**
 * Internal method to insert into a subtree.
 * @param x the item to insert.
 * @param t the node that roots the subtree.
 * @return the new root of the subtree.
 */
private BinaryNode<AnyType> insert( AnyType x, BinaryNode<AnyType> t )
{
    if( t == null )
        return new BinaryNode<AnyType>( x, null, null );

    int compareResult = x.compareTo( t.element );

    if( compareResult < 0 )
        t.left = insert( x, t.left );
    else if( compareResult > 0 )
        t.right = insert( x, t.right );
    else
        ;  // Duplicate; do nothing
    return t;
}

/**
 * Internal method to remove from a subtree.
 * @param x the item to remove.
 * @param t the node that roots the subtree.
 * @return the new root of the subtree.
 */
private BinaryNode<AnyType> remove( AnyType x, BinaryNode<AnyType> t )
{
    if( t == null )
        return t;   // Item not found; do nothing

    int compareResult = x.compareTo( t.element );

    if( compareResult < 0 )
        t.left = remove( x, t.left );
    else if( compareResult > 0 )
        t.right = remove( x, t.right );
    else if( t.left != null && t.right != null ) // Two children
    {
        t.element = findMin( t.right ).element;
        t.right = remove( t.element, t.right );
    }
    else
        t = ( t.left != null ) ? t.left : t.right;
    return t;
}

/**
 * Internal method to find the smallest item in a subtree.
 * @param t the node that roots the subtree.
 * @return node containing the smallest item.
 */
private BinaryNode<AnyType> findMin( BinaryNode<AnyType> t )
{
    if( t == null )
        return null;
    else if( t.left == null )
        return t;
    return findMin( t.left );
}

/**
 * Internal method to find the largest item in a subtree.
 * @param t the node that roots the subtree.
 * @return node containing the largest item.
 */
private BinaryNode<AnyType> findMax( BinaryNode<AnyType> t )
{
    if( t != null )
        while( t.right != null )
            t = t.right;

    return t;
}

/**
 * Internal method to find an item in a subtree.
 * @param x is item to search for.
 * @param t the node that roots the subtree.
 * @return node containing the matched item.
 */
private boolean contains( AnyType x, BinaryNode<AnyType> t )
{
    if( t == null )
        return false;

    int compareResult = x.compareTo( t.element );

    if( compareResult < 0 )
        return contains( x, t.left );
    else if( compareResult > 0 )
        return contains( x, t.right );
    else
        return true;    // Match
}

/**
 * Internal method to print a subtree in sorted order.
 * @param t the node that roots the subtree.
 */
private void printTree( BinaryNode<AnyType> t )
{
    if( t != null )
    {
        printTree( t.left );
        System.out.println( t.element );
        printTree( t.right ); 
    }
}

/**
 * Internal method for traversing the tree in-order.
 * @param t the node that roots the subtree.
 * @return 
 */
  private void nthElement(int n, BinaryNode<AnyType> t){

    int i = t.treeSize;
    if(t.left.treeSize == n){
        System.out.println(t.element);
    }else if(t.left.treeSize > n){
        nthElement(n, t.left);
    }else if(t.left.treeSize < n){
        int k = i - t.left.treeSize;
        nthElement(k, t.right);
    }
}

/** 
 * Internal method for finding tree size.
 * @param t the node that roots the subtree.
 * @return the number of nodes.
 */
private int treeSize(BinaryNode<AnyType> t){

    int size = 1;                                      
    if(t.right != null){
        size = size + treeSize(t.right);        
    }
    if(t.left != null){
        size = size + treeSize(t.left);          
    }
    return t.treeSize = size;
} 

/**
 * Internal method to compute height of a subtree.
 * @param t the node that roots the subtree.
 */
private int height( BinaryNode<AnyType> t )
{
    if( t == null )
        return -1;
    else
        return 1 + Math.max( height( t.left ), height( t.right ) );    
}

// Basic node stored in unbalanced binary search trees
private static class BinaryNode<AnyType>
{
        // Constructors
    BinaryNode( AnyType theElement )
    {
        this( theElement, null, null );
    }

    BinaryNode( AnyType theElement, BinaryNode<AnyType> lt, BinaryNode<AnyType> rt )
    {
        element  = theElement;
        left     = lt;
        right    = rt;
    }

    AnyType element;            // The data in the node
    BinaryNode<AnyType> left;   // Left child
    BinaryNode<AnyType> right;  // Right child
}

    // Test program
public static void main( String [ ] args )
{
    BinarySearchTree<Integer> t = new BinarySearchTree<Integer>( );
    final int NUMS = 10;
    final int GAP  = 1;

    System.out.println( "Checking... (no more output means success)" );

    t.insert(55);
    t.insert(40);
    t.insert(35);
    t.insert(60);
    t.insert(70);
    t.insert(80);

    System.out.println("this is tree size: " + t.treeSize());
    int n = t.root.left.treeSize;
    System.out.println(n);
    t.nthElement(3);

}
}

EDIT: I have modified the nthElement(int n, BinaryNode<AnyType> t) and treeSize(BinaryNode<AnyType t> methods. The problem now is that I get a NullPointerException for anything any number I put in other than 2 and 3.

Problem is you need both a count and node to be returned from the recursive function--or multiple functions. I'll do it in a way that would get you in trouble if you turned it in :)

Object nthElement(int n, BinaryNode t)
{
    // We are on the correct node, return it.
    if(n == 1) // I'll make this 1 based, so passing in 1 returns the first element. 
         return t;

    // Check the left side of the tree.
    if(t.left != null) {
        Object o=nthElement(n-1, t.left);
        // we found the correct node.
        if(o instanceof BinaryNode)
            return o;
        // we didn't find it but let's count the ones we found. (This is the "Trick")
        n=(Integer)o;
    }
    // We have no more children, let's just return our current count.
    if(t.right == null)
        return n;

    // Recurse right
    return(nthElement(n-1, t.right);
}

This is untested hand-coded and I often make huge logical errors on quick untested code, but the concept is sound. Any teacher worth his salt will probably fail this answer since the return value has two completely different unrelated types and I'm modifying a parameter, but I want to leave you with some of the fun!

The usage would have to check the return value, if it's an instanceof BinaryNode, great if not the tree didn't have enough nodes.

Also just for fun, I think that -(int)nthElement(0, t) counts the number of nodes in the tree.

A "Real" recursive solution would return a new mutable object with a BinaryNode and a count. As it is passed around you would modify the count, subtracting 1 for each node visited, when you hit 0 you return the object and extract it's "BinaryNode"

Your simplest (but least efficient) method would be something like:

// Ignoring the possibility that there may not be n elements in the tree.
int leftSize = treeSize(t.left);
// If the size of the left tree is greater than n then the nth element must be up the left branch.
if ( leftSize >= n ) {
  return nthElement(n-1, t.left);
} else {
  // Otherwise it must be up the right branch.
  return nthElement(n-leftSize, t.right);
}

However, it may be better to implement an Iterator and just step through it n times.

This works okay for me. Traverse the tree in order while keeping a count.

    int c = 0;
    public void findNth(int n, IntTree t) {

    if(!IntTree.isEmpty(t)) {
        findNth(n, t.left);
        c++;
        if(c==n)
            System.out.println("The element on position "+n+" is " + IntTree.value(t));
        findNth(n, t.right);

    }

I was struck solving this problem for two days. I was able to print the nth to last element. But had difficulties returning it. Finally able to solve using the below code:

    public class BinarySearchTree {

    Node root;

    public Node findNth(int n){
        if(root == null)
            return null;

        NodeCounter myNode = new NodeCounter();

        findNth(root,n, myNode);
        return myNode.node;
    }


    private void findNth(Node head, int n, NodeCounter nodeObj){

        if(head == null)
            return;

        findNth(head.left, n ,nodeObj);
        nodeObj.counter = nodeObj.counter + 1;
        if(n == nodeObj.counter){
            nodeObj.node = head;
            return;
        }
        if(n > nodeObj.counter)
            findNth(head.right,n,nodeObj);

    }

}

private class NodeCounter{
        Node node;
        int counter = 0;
}
class Node{
    Node left;
    Node right;
    int data;

    public Node getLeft() {
        return left;
    }

    public Node getRight() {
        return right;
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM