简体   繁体   English

Java中带有级别顺序的完整二进制搜索树

[英]A complete Binary Search Tree with level order insert in Java

We got an assignment where we need to code: 我们得到了一个需要编写代码的作业:

  • A Binary Search Tree 二叉搜索树
  • The Tree has to be complete , not perfect (which means all nodes which are not on the lowest level or second lowest level should have 2 children, while the nodes on the lowest level should be as far left as possible) 必须完整的 ,而不是完美的 (这意味着所有不在最低级别或第二低级别的节点都应有2个子节点,而最低级别的节点应尽可能远)
  • We need to insert to the tree in level order 我们需要按级别顺序插入树中
  • So if I have an Array with elements {0, 1, 2, 3, 4, 5, 6, 7} the root should be 4 , with 2, 1, 3, 0 on the left side, and 6, 5, 7 on the right side. 所以,如果我有元素的数组{0, 1, 2, 3, 4, 5, 6, 7}root应该是4 ,具有2, 1, 3, 0的左侧,和6, 5, 7在右侧。

  • The level order insert would be: 4, 2, 6, 1, 3, 5, 7, 0 级别顺序插入为: 4, 2, 6, 1, 3, 5, 7, 0

  • Just taking the middle of the Array and put it as root doesn't work. 仅将Array的中间部分放在根目录是行不通的。 If you got an array of 1 to 9 elements, you would have 4 as root (int value in java, double would be 4.5), and you would have 5 elements on the right side, and 4 on the left side. 如果您有一个由1到9个元素组成的数组,则您将有4个作为根(java中的int值,double是4.5),并且您将在右侧拥有5个元素,在左侧拥有4个元素。 This is not a complete tree. 这不是一棵完整的树。 Not even a perfect tree. 甚至不是完美的树。

在此处输入图片说明

My code is only able to insert at either left or right depending if it's greater og smaller than the root, no level order insertion. 我的代码只能在左边或右边插入,这取决于它是否大于根,或者不小于根,没有级别顺序插入。 The Anytype x parameter is the value to be inserted, while the BinaryNode t is the current node we are at in the tree (that's how we compare if we need to insert the new value to the left or right) Anytype x参数是要插入的值,而BinaryNode t是我们在树中的当前节点(如果需要在左侧或右侧插入新值,这就是我们进行比较的方式)

private BinaryNode<AnyType> insert( AnyType x, BinaryNode<AnyType> t )
{
    if( t == null )
        return new BinaryNode<>( 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;
}

How can I insert in level order and still maintain a binary search tree? 如何按级别顺序插入并仍然维护二进制搜索树? Should I use some form of recursion? 我应该使用某种形式的递归吗?

My whole program 我的整个程序

import java.nio.BufferUnderflowException;
import java.util.*;

import static java.lang.Math.pow;

/**
 * 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>>
{
    /**
     * 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 );
    }

    /**
     * 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 );
    }

    /**
     * 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<>( 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 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 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
    }

      /** The tree root. */
    private BinaryNode<AnyType> root;

        // Test program
    public static void main( String [ ] args )
    {
        BinarySearchTree<Integer> t = new BinarySearchTree<>( );

        t.insert(2);
        t.insert(1);
        t.insert(3);
        t.printTree();
    }
}

I think you should do it this manner (code is in C#, but shouldn't be very hard to convert it to Java): 我认为您应该以这种方式进行操作(代码在C#中,但是将其转换为Java并不难):

//list should be sorted
public void myFunc(List<int> list){
        Queue<List<int>> queue = new Queue<List<int>>();
         queue.Enqueue(list);

        while(queue.Any()){
            List<int> l = queue.Dequeue();
            int rindex = findRoot(l);
            //print r or store it somewhere
            Console.WriteLine(l.ElementAt(rindex));
            List<int> leftlist = l.GetRange(0, rindex);
            List<int> rightlist = l.GetRange(rindex + 1, l.Count-rindex-1);
            //leftlist = list l from index 0 to r-1; rightlist = list l from index r+1 to end.
            if (leftlist.Any())
            {
                queue.Enqueue(leftlist);
            }

            if (rightlist.Any())
            {
                queue.Enqueue(rightlist);
            }

        }

    }

******EDIT: ********************************************* ****** EDIT:********************************************** ***

For finding the root every time you have a list of size n, do the following: 为了在每次具有大小为n的列表时查找根,请执行以下操作:

public int findRoot(List<int> list)
        {
            int n = list.Count;

            double h = Math.Ceiling(Math.Log(n + 1, 2)) - 1;

            double totNodesInCompleteBinaryTreeOfHeighthMinusOne = Math.Pow(2, h) - 1;
            double nodesOnLastLevel = n - totNodesInCompleteBinaryTreeOfHeighthMinusOne;

            double nodesOnLastLevelInRightSubtree = 0;

            if (nodesOnLastLevel > Math.Pow(2, h - 1))
            {
                nodesOnLastLevelInRightSubtree = nodesOnLastLevel - Math.Pow(2, h - 1);
            }

            int rindex = (int)(n - nodesOnLastLevelInRightSubtree - 1 - ((totNodesInCompleteBinaryTreeOfHeighthMinusOne - 1) / 2));

            return rindex;
        }

The complete BST part took a bit of time to sort out what that actually is. 完整的BST部分花了一些时间来弄清实际内容。 Your requirement also calls for order level insert. 您的要求还要求插入订单级别。 I can't say this does "inserts", but it builds the BST in order level. 我不能说这确实可以“插入”,但是它可以按顺序构建BST。

The input List must be sorted first. 输入列表必须首先排序。

The building in order level is accomplished by taking the root and adding it to the BST, then splitting what is left into left and right lists, adding them into a list of lists, then processing the list of lists. 通过建立根并将其添加到BST,然后将剩余内容拆分左右列表,将它们添加到列表列表中,然后处理列表列表,可以完成顺序级别的构建。 Each round of splitting and adding to a list of lists is a level of insertion. 每轮拆分和添加到列表列表都是一个插入级别。

The complete part is more difficult, as was noticed. 正如所注意到的,整个部分比较困难。 The way to handle that is to compute the root for the list differently than a normal balanced tree. 处理该问题的方法是与普通平衡树不同地计算列表的根。 In a normal balanced tree the root index is at length/2. 在正常的平衡树中,根索引为length / 2。 For the BST to be complete the root index has to be offset so that nodes that would normally appear on the right side of the root instead appear on the left side of the root. 为了使BST完整,必须偏移根索引,以便通常会出现在根右侧的节点出现在根的左侧。 As long as the computation works for any length list then each split sublist is built properly. 只要计算适用于任何长度列表,那么每个拆分子列表都会正确构建。

From what I can tell computing the offset done by incrementing the offset for each additional element in length until you get to 1/2 of the width of a level. 据我所知,通过增加长度上每个其他元素的偏移量直到达到水平宽度的1/2来计算偏移量。 So, a BST with height of 4 has 8 elements in the lowest level. 因此,高度为4的BST在最低级别具有8个元素。 Lists of size 8, 9, 10, … 15 create BST with height of 4. For those lists the root index into the list is then 4, 5, 6, 7, 7, 7, 7, 7. 大小为8、9、10,……15的列表创建高度为4的BST。对于这些列表,列表的根索引为4、5、6、7、7、7、7、7。

Seems to work. 似乎可以工作。

public class Node<T extends Comparable<T>> {
    protected Node<T> left;
    protected Node<T> right;
    protected T data;   
}

public class BTree<T extends Comparable<T>> {
    private Node<T> root = new Node<>();
    public void addData(T data) {
        Node<T> parent = root;
        while (parent.data != null ) {
            if ( data.compareTo( parent.data ) > 0 ) {
                if ( parent.right == null ) 
                    parent.right = new Node<>();
                parent = parent.right;
            } else {
                if ( parent.left == null ) 
                    parent.left = new Node<>();
                parent = parent.left;
            }
        }
        parent.data = data;
    }
}

private void run() {
    for ( int i = 2; i < 65; ++i ) {
        List<Integer> intList = IntStream.range(1, i).boxed().collect(Collectors.toList());
        BTree<Integer> bTree = new BTree<>();
        List<List<Integer>> splitLists = new ArrayList<>();
        splitLists.add(intList);
        while (splitLists.size() > 0 ) {
            List<List<Integer>> tSplitLists = new ArrayList<>();
            for ( List<Integer> tIntList: splitLists) {
                int length = tIntList.size();
                // compute starting point
                int mid = calcMid(length);      // length/2 ; //+ calcOffset(length);
                bTree.addData(tIntList.get(mid));
                if ( mid - 0 > 0)
                    tSplitLists.add(tIntList.subList(0, mid));
                if ( length - (mid+1) > 0)
                    tSplitLists.add(tIntList.subList(mid+1, length));
            }
            splitLists = tSplitLists;
        }
        bTree.printNode();
    }
}
private int calcMid(int length) {
    if ( length <= 4 )
        return length / 2;
    int levelSize = 1;
    int total = 1;
    while ( total < length ) {
        levelSize *= 2;
        total += levelSize;
    }
    int excess = length - (total - levelSize);
    int minMid = (total - levelSize + 1) / 2;
    if ( excess <= levelSize / 2 ) {
        return minMid + (excess - 1); 
    } else {
        int midExcess = levelSize/2; 
        return minMid + (midExcess - 1);
    }

}

See How to print binary tree diagram? 请参阅如何打印二叉树图? for code on printing a binary tree. 用于打印二叉树的代码。

PS> I'm sure you can make it a bit better by clearing and copying Lists instead of making new ones each time. PS>我相信您可以通过清除和复制列表而不是每次都创建新列表来使它更好。

EDIT: Did you go and get the printNode code referenced above? 编辑:你去获取printNode代码了吗?

1 

 2   
/   
1   

 2   
/ \ 
1 3

   3       
  / \   
 /   \  
 2   4   
/       
1

And so on … 等等 …

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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