AVL树:如何正确重新平衡?

时间:2015-06-19 02:35:17

标签: java avl-tree tree-balancing

我正在编写一个AVL Tree类,我的教授已经为我们的代码添加了一些测试。我已经通过了除三个以外的所有测试,而我失败的三个测试似乎表明,当元素被添加到树中或从树中移除时,树在必要时不会自行平衡。我一直在尝试调试它,但我似乎无法找到错误。有人可以指出我错过了什么吗?

修改 测试失败: RemoveRoot - 删除AVL树的根并将其替换为其后继者。 AddManySingle - 添加多个元素并强制单个旋转。 AddManyDouble - 添加多个元素并强制双重旋转。

private int count, height;
private AVLNode < E > root;

public AVLTree() {
    root = null;
    count = 0;
    height = 0;
}

//the private inner Node class
private class AVLNode < E > {

    private int height, bf;
    private E data;
    private AVLNode < E > left, right;

    public AVLNode() {
        data = null;
        left = right = null;
        bf = 0;
        height = 0;
    }

    public AVLNode(E data) {
        this.data = data;
        left = right = null;
        bf = 0;
        height = 0;
    }

    public E getData() {
        return data;
    }

    public void setData(E data) {
        this.data = data;
    }

    public AVLNode < E > getLeft() {
        return left;
    }

    public void setLeft(AVLNode < E > left) {
        this.left = left;
    }

    public AVLNode < E > getRight() {
        return right;
    }

    public void setRight(AVLNode < E > right) {
        this.right = right;
    }

    public int getBF() {
        return bf;
    }

    public void setBF() {
        if (right == null && left == null) {
            bf = 0;
        } else if (right == null) {
            bf = this.getLeft().getHeight();
        } else if (left == null) {
            bf = 0 - this.getRight().getHeight();
        } else {
            this.bf = this.getLeft().getHeight() - this.getRight().getHeight();
        }
    }

    public void setBF(int theBalanceFactor) {
        bf = theBalanceFactor;
    }

    /**
     * The getHeight method computes the height of an AVL tree.
     * @return The height of the AVL tree.
     */
    public int getHeight() {
        return this == null ? -1 : this.height;
    }

    public void setHeight() {
        int leftHeight = (left == null) ? -1 : left.getHeight();
        int rightHeight = (right == null) ? -1 : right.getHeight();
        height = 1 + Math.max(leftHeight, rightHeight);
    }

    public void setHeight(int theHeight) {
        height = theHeight;
    }
}

//the private inner Iterator class
private class AVLIterator implements Iterator < E > {

    private AVLNode < E > nextNode;
    private Stack < AVLNode < E >> stack;


    public AVLIterator() {
        nextNode = root;
        stack = new Stack < AVLNode < E >> ();
        while (root != null) {
            stack.push(root);
            root = root.getLeft();
        }
    }

    //Returns true if the iteration has more datas.
    @Override
    public boolean hasNext() {
        return !stack.isEmpty();
    }

    //Returns the next data in the iteration.
    @Override
    public E next() {
        if (!hasNext()) {
            throw new NoSuchElementException("No next element.");
        }
        AVLNode < E > node = stack.pop();
        E result = node.data;
        if (node.right != null) {
            node = node.right;
            while (node != null) {
                stack.push(node);
                node = node.left;
            }
        }
        return result;
    }

    //Removes the last data in the iteration.
    @Override
    public void remove() {
        throw new UnsupportedOperationException("Invalid operation for support list.");
    }
}

/**
 * Rotate binary tree node that has a right child.
 * Update heights, then return new root.
 * n1 is the imbalanced node
 */
private AVLNode < E > rotateWithRightChild(AVLNode < E > n1) {
    AVLNode < E > n2 = n1.right;
    n1.right = n2.left;
    n2.left = n1;
    return n2;
}

/**
 * Rotate binary tree node that has a left child.
 * Update heights, then return new root.
 */
private AVLNode < E > rotateWithLeftChild(AVLNode < E > n2) {
    AVLNode < E > n1 = n2.left;
    n2.left = n1.right;
    n1.right = n2;
    return n1;
}

/**
 * Double rotate binary tree node: first left child
 * with its right child; then node n3 with new left child.
 * Update heights, then return new root.
 */
private AVLNode < E > doubleWithLeftChild(AVLNode < E > n3) {
    n3.left = rotateWithRightChild(n3.left);
    return rotateWithLeftChild(n3);
}

/**
 * Double rotate binary tree node: first right child
 * with its left child; then node n1 with new right child.
 * Update heights, then return new root.
 */
private AVLNode < E > doubleWithRightChild(AVLNode < E > n1) {
    n1.right = rotateWithLeftChild(n1.right);
    return rotateWithRightChild(n1);
}

//updates height and balance factor
private void updateHeightAndBF(AVLNode < E > node) {
    int leftHeight, rightHeight, bf = 0, height = 0;
    if (node != null) {
        if (node.getLeft() == null) {
            leftHeight = -1;
        } else {
            leftHeight = node.left.height;
        }
        if (node.getRight() == null) {
            rightHeight = -1;
        } else {
            rightHeight = node.right.height;
        }
        /*if (leftHeight >= rightHeight) {
                height = leftHeight + 1;
            } else if (rightHeight >= leftHeight) {
                height = rightHeight + 1;
            }*/
        height = Math.max(leftHeight, rightHeight) + 1;
        bf = leftHeight - rightHeight;
        node.setHeight(height);
        node.setBF(bf);
    } else {
        height = -1;
    }
}

/**
 * private helper method.
 * Balances a node by updating balance
 * factor when a node is removed from
 * or added to the AVL tree.
 *
 * @param node the node that is being balanced
 */
private AVLNode < E > balance(AVLNode < E > node) {
    int hLeft, hRight;
    if (node == null) {
        return node;
    }
    if (node.bf == 2) {
        if (node.left.bf == 1) {
            node.left = rotateWithLeftChild(node);
        } else if (node.left.bf == -1) {
            node.left = doubleWithLeftChild(node);
        }
    } else if (node.bf == -2) {
        if (node.right.bf < 0) {
            node.right = rotateWithRightChild(node);
        } else if (node.right.bf == -1) {
            node.right = doubleWithRightChild(node);
        }
    }
    return node;

}

/**
 * Adds the item to the tree.  Duplicate items and null items should not be added. O(log n)
 *
 * @param item the item to add
 * @return true if item added, false if it was not
 */
 @Override
public boolean add(E item) { // TODO
    int theSize = size();
    if (root == null && item != null) { //null check here for item
        root = new AVLNode < E > (item);
        count++;
        return true;
    } else if (item == null) {
        return false;
    } else {
        add(root, item);
        root = balance(root);
    }
    return (count != theSize);
}

/**
 * private helper method for the add() method.
 * Adds the item to the tree.  Duplicate items and null items should not be added.
 * Runs in O(log n) expected time, may be linear time in worst case
 *
 * @param value the item to add
 * @return true if item added, false if it was not
 */
private boolean add(AVLNode < E > node, E value) { // TODO
    if (value.equals(node.getData()) || value == null) {
        //if it's a duplicate or null
        return false;
    }
    if (value.compareTo(node.data) < 0) {
        AVLNode < E > left = node.left;
        if (left == null) {
            node.left = new AVLNode < E > (value);
            count++;
            updateHeightAndBF(node);
            balance(node);
            return true;
        } else updateHeightAndBF(node);
        balance(node);
        return add(left, value);
    } else if (value.compareTo(node.data) > 0) {
        AVLNode < E > right = node.right;
        if (right == null) {
            node.right = new AVLNode < E > (value);
            count++;
            updateHeightAndBF(node);
            balance(node);
            return true;
        } else {
            updateHeightAndBF(node);
            balance(node);
            return add(right, value);
        }
    }
    return false;
}

/**
 * returns the maximum data held in the tree. null if tree is empty.
 * runs in O(log n) expected, may be linear in worst case
 *
 * @return maximum item or null if empty
 */
 @Override
public E max() {
    if (isEmpty()) {
        return null;
    }
    return max(root).data;
}

/**
 * a private helper method for the max() method.
 *
 * @return maximum Node or null if empty
 */
private AVLNode < E > max(AVLNode < E > x) {
    if (x.getRight() == null) { //the rightmost node from the root
        //is larger than the root, which is larger than the leftmost
        //node from the root. so, if the rightmost node is null,
        // that means the root node is the maximum data in the tree
        return x;
    } else {
        return max(x.getRight());
    }
}

/**
 * returns the number of items in the tree O(1) with variable
 *
 * @return the number of items in the tree
 */
 @Override
public int size() {
    return count;
}

/**
 * O(1)
 * @return true if tree has no datas, false if tree has anything in it.
 */
 @Override
public boolean isEmpty() {
    return size() == 0;
}

/**
 * runs in O(n) worst case, and O(log n) average case.
 * @return the minimum data in the tree or null if empty
 */
 @Override
public E min() {
    if (isEmpty()) {
        return null;
    }
    return min(root).data;
}

/**
 * private helper method for the min() method
 *
 * @return the minimum Node in the tree or null if empty
 */
private AVLNode < E > min(AVLNode < E > y) {
    if (y == null) {
        return null;
    } else if (y.left == null) { //the leftmost node from the root
        //is smaller than the root, which is smaller than the rightmost
        //node. so, if the leftmost node is null,
        // that means the root node is the minimum data in the tree
        return y;
    } else {
        return min(y.left);
    }
}

/**
 * Checks for the given item in the tree. O(log n)
 *
 * @param item the item to look for
 * @return true if item is in tree, false otherwise
 */
 @Override
public boolean contains(E item) {
    return contains(root, item);
}

/**
 * private helper method for the contains() methods
 * Checks for the given item in the tree.
 * runs in O(log n) expected, may be linear in worst case
 *
 * @param root the starting point
 * @param item the item to look for
 * @return true if item is in tree, false otherwise
 */
private boolean contains(AVLNode < E > root, E item) {
    if (root == null || isEmpty()) {
        return false;
    }
    if (item.compareTo(root.getData()) == 0) { //if item is
        //equal to the data in the root Node, return true
        return true;
    } else {
        if (item.compareTo(root.getData()) < 0) { //if item is less than data
            AVLNode < E > left = root.getLeft(); //go to the left node
            return (contains(left, item)); //call contains() method with the left node
        } else if (item.compareTo(root.getData()) > 0) { //if item is more than data
            AVLNode < E > right = root.getRight(); //go to the right node
            return (contains(right, item)); //call contains() method with the right node
        } else {
            return true;
        }
    }
}

/**
 * removes the given item from the tree O(log n). if the item is found
 * and removed, balance the tree
 *
 * @param item the item to remove
 * @return true if item removed, false if item not found
 */
 @Override
public boolean remove(E item) {
    int theSize = size();
    root = remove(root, item);
    return (count != theSize);
}

/**
 * private helper method for the remove() method
 * removes the given item from the tree
 * runs in O(log n) expected, may be linear in worst case
 * uses in-order successor
 *
 * @param node the starting point
 * @param item the item to remove
 * @return the node if the specified is node in the tree, null otherwise
 */
private AVLNode < E > remove(AVLNode < E > node, E item) { //TODO
    if (node == null || item == null) {
        return null;
    }
    if (root == null) {
        return root;
    }
    if (item.compareTo(node.data) < 0) {
        updateHeightAndBF(node);
        balance(node);
        node.left = remove(node.left, item);
    } else if (item.compareTo(node.data) > 0) {
        updateHeightAndBF(node);
        balance(node);
        node.right = remove(node.right, item);
    } else if (node.left != null && node.right != null) { //two child nodes
        AVLNode < E > succ = min(node.right);
        node.data = succ.data;
        updateHeightAndBF(node);
        balance(node);
        node.right = remove(node.right, node.data);
    } else {
        node = (node.left != null) ? node.left : node.right;
        count--;
        updateHeightAndBF(node);
        return balance(node);
    }
    return node;
}

/**
 * Runs in linear time, O(n)
 * @return a list of the data in post-order traversal order
 */
 @Override
public List < E > getPostOrder() {
    List < E > postOrderList = new ArrayList < E > (size());
    recPostOrder(root, postOrderList);
    return postOrderList;
}

/**
 * private helper method for the getPostOrder() method
 * Runs in linear time, O(n)
 */
private void recPostOrder(AVLNode < E > theRoot, List < E > theList) {
    if (theRoot != null) {
        recPostOrder(theRoot.left, theList);
        recPostOrder(theRoot.right, theList);
        theList.add(theRoot.data);
    }
}

/**
 * Runs in linear time, O(n)
 * @return a list of the data in pre-order traversal order
 */
 @Override
public List < E > getPreOrder() {
    List < E > preOrderList = new ArrayList < E > (size());
    recPreOrder(root, preOrderList);
    return preOrderList;

}

/**
 * private helper method for the getPreOrder() method
 * Runs in linear time, O(n)
 *
 */
private void recPreOrder(AVLNode < E > theRoot, List < E > theList) {
    if (theRoot != null) {
        theList.add(theRoot.getData());
        recPreOrder(theRoot.getLeft(), theList);
        recPreOrder(theRoot.getRight(), theList);
    }
}

/**
 * Runs in linear time
 * @return a list of the data in pre-order traversal order
 */
public List < E > getInOrder() {
    List < E > inOrderList = new ArrayList < E > (size());
    recInOrder(root, inOrderList);
    return inOrderList;
}

/**
 *
 * private helper method for the getInOrder() method
 * Runs in linear time
 *
 */
private void recInOrder(AVLNode < E > theRoot, List < E > theList) {
    if (theRoot != null) {
        recInOrder(theRoot.left, theList);
        theList.add(theRoot.data);
        recInOrder(theRoot.right, theList);
    }
}

/**
 * Runs in linear time, O(n)
 * @return a list of the data in level-order traversal order
 */
 @Override
public List < E > getLevelOrder() {
    List < E > data = new ArrayList < E > ();
    Queue < AVLNode < E >> queue = new LinkedList < AVLNode < E >> ();
    if (this.isEmpty()) {
        return data;
    } else {
        queue.add(root);
    }
    while (!queue.isEmpty()) {
        AVLNode < E > x = queue.remove();
        if (x != null) {
            data.add(x.getData());
            if (x.getLeft() != null) {
                queue.add(x.getLeft());
            }
            if (x.getRight() != null) {
                queue.add(x.getRight());
            }
        }
    }
    return data;
}

/**
 * O(1) [ignore garbage collection costs]
 *
 * Removes all the datas from this tree
 */
 @Override
public void clear() {
    root = null;
    count = 0;
}


/**
 * returns an iterator over this collection
 * iterator is based on an in-order traversal
 */
 @Override
public Iterator < E > iterator() {
    return new AVLIterator();
}
}

0 个答案:

没有答案