【问题标题】:Java - Balance a Binary Tree with "Brute Force"Java - 用“蛮力”平衡二叉树
【发布时间】:2016-03-19 02:46:22
【问题描述】:

我正在尝试用Java编写一个平衡二叉树的方法,描述如下:

... 编写树的中序遍历 到一个数组,然后使用递归方法(很像二分查找)将数组的中间元素作为根插入,然后构建平衡的左右子树。

但是,我对如何一起完成这一切感到困惑。到目前为止,我已经尝试了多种方法,但没有任何效果。

我也已经有一个 inOrder 迭代器,它返回树中所有元素的 ArrayList,所以已经涵盖了。

这是我目前正在构建的:

public void rebalance()
{
    Iterator<T> it = iteratorInOrder();
    List<T> list = new ArrayList<>();

    while (it.hasNext())
    {
        list.add(it.next());
    }
    balanceRecursive( );
}

private void balanceRecursive( )
{
    //something here
}

然后我只是在添加/删除元素方法的末尾有这个:

if ((getHeight() - getMinHeight()) > 5)
            rebalance();

那么,我该怎么做呢?

【问题讨论】:

  • 不需要再平衡 - 只需构建。 (这个任务本身很有趣——你可以在给定项目数量和单调序列的项目的情况下构建一个平衡的搜索树,不需要随机访问。对于有序数组,构建树的原因是支持更新。)

标签: java algorithm recursion tree binary-search-tree


【解决方案1】:

一旦您通过按顺序遍历获得List,这将变得相对简单。 sublist 操作意味着您甚至不需要传递索引:

Node<T> buildBalancedTree(List<T> values) {
    if (values.isEmpty()) {
        return Node.NULL;
    } else {
        int middle = values.size() / 2;
        Node node = new Node(values.get(middle));
        node.setLeft(buildBalancedTree(values.subList(0, middle)));
        node.setRight(buildBalancedTree(values.subList(middle + 1, values.size())));
        return node;
    }
}

我假设您有一个 Node.NULL 来表示空子树而不是“空”,因为您应该 :-)

所以你的再平衡方法看起来像:

root = buildBalancedTree(root.getListOfValuesInOrder());

【讨论】:

  • 工作就像一个魅力,我明白我在哪里想错了。谢谢。
【解决方案2】:
  1. 构建一个以中间数组元素为值的节点。
  2. 递归地将 (1) 应用于数组的左侧部分,并将如此构造的节点设置为根节点的左子节点。
  3. 将 (1) 应用于数组的右侧部分,递归,并将如此构造的节点设置为根节点的右子节点。

【讨论】:

    【解决方案3】:

    在平衡树中,左右子树的大小最多应相差 1 个元素。给定一个排序数组——由你的中序遍历产生——这变得非常简单。基本思想是构建一棵实际的树,它等于二分搜索隐式遍历的树。以数组的中间元素为根开始,中间左边的子数组的中间元素成为左孩子,右孩子的模式相同。根据数组的左半部分,以左子树为根构建子树,右子树也是如此。

    //lower and upper are the INCLUSIVE bounds of the sublist, from which the tree should be built
    Node<T> buildRecursively(int lower , int upper , List<T> elements){
        //no more nodes can be produced on this path
        if(lower > upper)
            return null;
    
        //build node from the middle-element of the elements
        int middle = (lower + upper) / 2;
        Node<T> result = new Node<>(elements.get(middle));
    
        //build left and right child for the according pieces of the array/list
        result.setLeftChild(buildRecursively(lower , middle - 1 , elements);
        result.setRightChild(buildRecursively(middle + 1 , upper , elements);
    
        return result;
    }
    
    //build entire tree:
    Node<T> root = buildRecursively(0 , elements.size() - 1 , elements);
    

    对于一个实际的数组,它看起来像这样:

                 sorted ascending--------->
                             4
                    2                6
                 1     3          5     7
    indices:     0  1  2     3    4  5  6
    

    【讨论】:

    • 您可以通过使用subList 而不是传递索引来简化此操作。 subList 返回视图而不是副本,因此它同样高效且易于阅读(在我看来)。
    • @sprinter 好吧,每个人都有自己的喜好。我更喜欢这个,因为没有创建一次性对象。我使用了以边界作为参数的方法,因为它更接近二分搜索(我在答案的开头提到了它)。
    • 这绝对帮助我弄清楚我做错了什么,但是实施它给了我一个 StackOverflow 错误,从 'result.setLeftChild' 行开始。无论如何,谢谢。
    • @Mucker 我忘了加括号。这只是一个简单的算术错误。我已经更正了。
    • @Paul 我很好奇 - 你在评论中提到的一次性物品是什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 2018-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    相关资源
    最近更新 更多