简体   繁体   English

AVL 树实现 - 不存储高度

[英]AVL Tree Implementation - Without Storing Height

I'm currently in the middle of an AVL Tree insert implementation, and I am struggling with maintaining the balance factors while inserting and backtracking up the tree.我目前正处于 AVL 树插入实现的中间,并且在插入和回溯树时,我正在努力保持平衡因素。

Virtually every AVL implementation I can find as an example uses the height of a node's two sub-tree's to calculate the balance factor, something along the lines of实际上,我可以找到的每个 AVL 实现作为示例都使用节点的两个子树的高度来计算平衡因子,类似于

node.balance = node.right.height - node.left.height

And this is perfectly fine if your Node class looks something like如果您的节点 class 看起来像

class Node {
    int value, height;
    Node left, right;
}

Though the problem is that for this particular implementation, it is 'against the rules' to keep track of the height of the node, and instead we can only keep track of the balance factor.尽管问题在于对于这个特定的实现,跟踪节点的高度是“违反规则的”,而我们只能跟踪平衡因子。 So the Node class instead looks like所以节点 class 看起来像

class Node {
    int value, balance;
    Node left, right;
}

I know that maintaining the balance factor of a node is conceptually similar to maintaining the height for each insert into the tree, but for the life of me I can't figure out all of the situations in which the balance factor should change for a particular node.我知道保持节点的平衡因子在概念上类似于保持每个插入树的高度,但是对于我的一生,我无法弄清楚平衡因子应该为特定变化而改变的所有情况节点。

At the moment I've got setting the balance factor implemented by instead recursively calling the height function for each and every node ( not optimal. ) to make sure my rotations and general insertion is correct.目前我已经通过递归调用每个节点的高度 function 来设置平衡因子(不是最佳的。),以确保我的旋转和一般插入是正确的。

node.balance = height(node.right) - height(node.left)

Where height() recursively traverses the tree to find the longest path to a leaf. height()递归遍历树以找到到叶子的最长路径。

And I have verified that the rotation logic is indeed correct, but when I start writing code to maintain the balances by +-1 increments backtracking up the tree, the code immediately turns into spaghetti, as I am clearly not understanding something fundamental about the node balance factor.而且我已经验证了旋转逻辑确实是正确的,但是当我开始编写代码以通过 +-1 增量向树回溯来保持余额时,代码立即变成了意大利面条,因为我显然不了解节点的基本知识平衡因素。

If you want to see that code, I've posted it below ( its a bit long ).如果您想查看该代码,我已将其发布在下面(它有点长)。 And the below implementation is also a String AVL Tree, but the idea is the same.并且下面的实现也是一个String AVL Tree,但是思路是一样的。

Any input is appreciated, Thanks!任何输入表示赞赏,谢谢!

class StringAVLNode {
    private String item;
    private int balance;
    private StringAVLNode left, right;

    // just one constructor, please
    public StringAVLNode(String str) {
        item = str;
        balance = 0;
        left = null; right = null;
    }

    public int getBalance () {
        return balance;
    }

    public void setBalance ( int bal){
        balance = bal;
    }

    public String getItem () {
        return item;
    }

    public StringAVLNode getLeft () {
        return left;
    }

    public void setLeft (StringAVLNode pt){
        left = pt;
    }

    public StringAVLNode getRight () {
        return right;
    }

    public void setRight (StringAVLNode pt){
        right = pt;
    }



    public void insert(String str) {
        root = insert(str, root);
    }


    private StringAVLNode insert(String str, StringAVLNode t) {

        // Base case - Just insert the node
        if (t == null)
            t = new StringAVLNode(str);
        else {
            int balance, leftChildBalance, rightChildBalance;
            leftChildBalance = t.getLeft() != null ? t.getLeft().getBalance() : -99;
            rightChildBalance = t.getRight() != null ? t.getRight().getBalance() : -99;

            // Perform string comparisons to determine left/right insert
            int compareResult = str.compareToIgnoreCase(t.getItem());
            if (compareResult < 0) {
                t.setLeft(insert(str, t.getLeft()));

                if (t.getRight() == null)
                    t.setBalance(t.getBalance()-1);
                else if (leftChildBalance == 0 && t.getLeft().getBalance() != 0)
                    t.setBalance(t.getBalance()-1);
                else if (leftChildBalance == -99 && t.getLeft() != null)
                    t.setBalance(t.getBalance()-1);

            }
            else if (compareResult > 0) {
                t.setRight(insert(str, t.getRight()));


                if (t.getLeft() == null)

                    t.setBalance(t.getBalance()+1);
                else if (rightChildBalance == 0 && t.getRight().getBalance() != 0)
                    t.setBalance(t.getBalance()+1);
                else if (rightChildBalance == -99 && t.getRight() != null)
                    t.setBalance(t.getBalance()+1);
            }
            balance = t.getBalance();


            // Verbosify booleans
            boolean rightImbalance = balance > 1; boolean leftImbalance = balance < -1;

            // Imbalance tree situation calls balanceTrees() to handle the rotation logic
            // ( Keeps insert() succinct )
            if (rightImbalance || leftImbalance)
                t = balanceTrees(balance, t);

        }
        return t;
    }



    // Rotation Handler
    private StringAVLNode balanceTrees(int balance, StringAVLNode t) {

        // Verbosify boolean values
        boolean rightHeavy = balance > 1; boolean leftHeavy = balance < -1;
        boolean requiresDoubleLeft = t.getRight() != null && t.getRight().getBalance() <= -1;
        boolean requiresDoubleRight = t.getLeft() != null && t.getLeft().getBalance() >= 1;

        if (rightHeavy) {

            /** Do double left rotation by right rotating the right child subtree, then
             * rotate left
             */
            if (requiresDoubleLeft) {

                t.setRight(rotateRight(t.getRight()));
                t.getRight().setBalance(0);
                t = rotateLeft(t);
                t.setBalance(0);

            }
            else {
                t = rotateLeft(t);
                t.setBalance(0);
                if (t.getLeft() != null) t.getLeft().setBalance(0);
                if (t.getRight() != null) t.getRight().setBalance(0);
            }
        }

        /** Do double right rotation by left rotating the left child subtree, then
         * rotate right
         */
        else if (leftHeavy) {
            if (requiresDoubleRight) {

                t.setLeft(rotateLeft(t.getLeft()));
                t.getLeft().setBalance(0);
                t = rotateRight(t);
                t.setBalance(0);

            }
            else {
                t = rotateRight(t);
                t.setBalance(0);
                if (t.getLeft() != null) t.getLeft().setBalance(0);
                if (t.getRight() != null) t.getRight().setBalance(0);
            }
        }
        if (t.getLeft() != null) {
            if (t.getLeft().getRight() != null && t.getLeft().getLeft() == null)
                t.getLeft().setBalance(1);
            else if (t.getLeft().getLeft() != null && t.getLeft().getRight() == null)
                t.getLeft().setBalance(-1);
            else if ((t.getLeft().getLeft() != null && t.getLeft().getRight() != null)
                    || (t.getLeft().getLeft() == null && t.getLeft().getRight() == null))
                t.getLeft().setBalance(0);
        }
        if (t.getRight() != null) {
            if (t.getRight().getRight() != null && t.getRight().getLeft() == null)
                t.getRight().setBalance(1);
            else if (t.getRight().getLeft() != null && t.getRight().getRight() == null)
                t.getRight().setBalance(-1);
            else if ((t.getRight().getLeft() != null && t.getRight().getRight() != null)
                    || (t.getRight().getLeft() == null && t.getRight().getRight() == null))
                t.getRight().setBalance(0);
        }
        return t;
    }
}

Check out my AVL Tree in Java writeup at:在 Java 的文章中查看我的 AVL 树:

https://debugnotes.wordpress.com/2015/01/07/implementing-an-avl-tree-in-java-part-2 https://debugnotes.wordpress.com/2015/01/07/implementing-an-avl-tree-in-java-part-2

It appears that your implementation does not include any kind of stack based element (recursive or array based) to keep track of how deep you are in the tree.您的实现似乎不包括任何类型的基于堆栈的元素(递归或基于数组)来跟踪您在树中的深度。 This is a key part of being able to navigate self-balancing tree data structures - being able to search downwards, find and do something with a target node, and then trace backwards to the root node of the tree where it started navigating from, manipulating it as you work your back way up.这是能够导航自平衡树数据结构的关键部分 - 能够向下搜索,找到目标节点并对其执行某些操作,然后向后追踪到它开始导航的树的根节点,操作当你向后工作时它。 Using recursion is one way (ie using the program stack) or you need to implement your own stack (eg use a Queue or LinkedList) but unless your code has a memory structure recording where it's been, unfortunately it'll always get lost.使用递归是一种方法(即使用程序堆栈),或者您需要实现自己的堆栈(例如使用队列或链接列表),但除非您的代码有一个 memory 结构记录它所在的位置,不幸的是它总是会丢失。

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

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