Scapegoat tree - also a kind of elegant violence

As a binary search tree, then the most important thing is how to keep their balance, in order to maintain balance, binary search trees are Eight Immortals recount , such as AVL trees, red-black tree, Treap trees, splay trees, etc., but the original aim, their methods are based on the rotation , and then change the relationship between the nodes.

In particular, some binary search tree implementation is very tedious, like red-black tree, add and delete nodes need to deal with a total of about a dozen cases, finished debug been evaluated days have been dark times.

The scapegoat tree is an unusual tree, when met imbalance will not find ways to adjust the balance, direct violence on her reconstruction.

reconstruction

image.png

Above tree sub-tree, it is clearly unbalanced, though they did not know on what conditions to determine whether the balance. We directly subtree tree shoot flat, arranged from small to large (in preorder).

image.png

The middle of the root element as a new element on both sides were as a child. So for her reconstruction is complete, this feels like to be picked up from the middle, as both sides droop down. After the reconstruction of binary basic binary tree is full , and very efficient.

image.png

So scapegoat tree is how to determine whether a tree needs to balance it. Very simple, each tree will take a balance factor alpha, in the range of between 0.5 to 1. If the total number of nodes in a tree * alpha <summarize some point the child tree, it is unbalanced. Most figure above example, to the root node 6 of a total of seven sub-tree nodes, the left child is 5 to 6 sub-tree root, a total of five nodes, alpha is assumed to take 0.7, 0.7 7 * <5, So it is uneven.

Values ​​for alpha, alpha, if smaller, then the more demanding balance, reconstruction will be more times; alpha greater degree balanced tree will be reduced, the number of reconstruction is also reduced. In general, alpha take relatively modest 0.7.

insert

Insertion ordinary binary beginning no difference, the values into the appropriate leaf node, begins to adjust the balance. If the self-inserted node and from the adjustment, the adjustment after completion deeper level sub-tree back up, if lower levels of the tree does not meet the balance, all of the sub-tree still need to be rebuilt, so there are a lot of reconstruction is meaningless of. Therefore, reconstruction should start from the root, first down to determine the need to rebuild. Analyzing all the nodes is not required, only we need to determine the path from the root node to the leaf node to the newly inserted in elapsed.

So long as the occurrence of a reconstruction do not recursively downward, so the insertion of any of a number, up to occur once reconstruction .

delete

Delete There are many kinds of practices:

  1. Each whether to delete a node, will conduct a top-down judgments need to be rebuilt.

  2. Each delete a node does not actually delete, mark it just does not participate in search. When the ratio of a sub-tree in the deleted node is greater than a certain value directly reconstruction, this ratio can take direct alpha, it can be freely controlled by us.

  3. Each delete a node does not actually delete, mark it just does not participate in search. When an insert causes a trigger is no longer balanced reconstruction, the way will be marked for deletion of nodes move out not participate in the reconstruction.

The second and the third way not very different ways, are idle deleted , the specific use of which way will do.

Code

Implements only the insertion, deletion will follow-up full.

Tree node structure
public class ScapegoatTreeNode<E> {
        // 以此节点为根的子树的总节点个数
        private int size = 1;
        private E value;
        private ScapegoatTreeNode<E> leftChild;
        private ScapegoatTreeNode<E> rightChild;
        ScapegoatTreeNode(E value) {
            this.value = value;
        }

        public int getSize() {
            return size;
        }

        public void setSize(int size) {
            this.size = size;
        }

        public E getValue() {
            return value;
        }

        public void setValue(E value) {
            this.value = value;
        }

        public ScapegoatTreeNode<E> getLeftChild() {
            return leftChild;
        }

        public void setLeftChild(ScapegoatTreeNode<E> leftChild) {
            this.leftChild = leftChild;
        }

        public ScapegoatTreeNode<E> getRightChild() {
            return rightChild;
        }

        public void setRightChild(ScapegoatTreeNode<E> rightChild) {
            this.rightChild = rightChild;
        }
    }
Insert
public class ScapegoatTree<E extends Comparable<E>> {

    private ScapegoatTreeNode<E> root;
    private static final double ALPHA_MAX = 1;
    private static final double ALPHA_MIN = 0.5;
    private double alpha = 0.7;

    private List<ScapegoatTreeNode<E>> insertPath = new ArrayList<>();

    public ScapegoatTree() {
    }

    public ScapegoatTree(double alpha) {
        if (alpha < 0.5) {
            alpha = 0.5;
        }
        if (alpha > 1) {
            alpha = 0.99;
        }
        this.alpha = alpha;
    }

    public void insert(E value) {
        ScapegoatTreeNode<E> node = new ScapegoatTreeNode<>(value);
        if (root == null) {
            root = new ScapegoatTreeNode<>(value);
        } else {
            boolean successfullyInsertion = insertValue(root, node);
            if (successfullyInsertion) {
                insertPath.forEach(node->node.size++);
                tryAdjust();
            }
            clearInsertPath();
        }
    }

    private boolean insertValue(ScapegoatTreeNode<E> parent, ScapegoatTreeNode<E> node) {
        if (parent == null || node == null) {
            return false;
        }
        insertPath.add(parent);
        int com = node.getValue().compareTo(parent.getValue());
        if (com < 0) {
            if (parent.getLeftChild() != null) {
                return insertValue(parent.getLeftChild(), node);
            } else {
                parent.setLeftChild(node);
                return true;
            }
        } else if (com > 0) {
            if (parent.getRightChild() != null) {
                return insertValue(parent.getRightChild(), node);
            } else {
                parent.setRightChild(node);
                return true;
            }
        }
        return false;
    }

    private void tryAdjust() {
        for (int i = 0; i < insertPath.size(); i++) {
            ScapegoatTreeNode<E> node = insertPath.get(i);
            int leftChildNodeCount = Optional.ofNullable(node.getLeftChild())
                    .map(left -> left.size)
                    .orElse(0);
            if (leftChildNodeCount > (int)(node.size * alpha) || leftChildNodeCount < (int)(node.size * (1 - alpha))) {
                rebuild(node, i == 0 ? null : insertPath.get(i - 1));
                return;
            }
        }
    }

    private void rebuild(ScapegoatTreeNode<E> root, ScapegoatTreeNode<E> parent) {
        List<E> elements = new ArrayList<>();
        inOrderTraversal(root, elements);

        ScapegoatTreeNode<E> newRoot = reBuildCore(elements,0, elements.size() - 1);
        if (parent == null) {
            this.root = newRoot;
        } else if (parent.getLeftChild() == root) {
            parent.setLeftChild(newRoot);
        } else {
            parent.setRightChild(newRoot);
        }
    }

    private void inOrderTraversal(ScapegoatTreeNode<E> root, List<E> elements) {
        if (root == null) {
            return;
        }
        inOrderTraversal(root.getLeftChild(), elements);
        elements.add(root.getValue());
        inOrderTraversal(root.getRightChild(), elements);
    }

    private ScapegoatTreeNode<E> reBuildCore(List<E> elements, int start, int end) {
        if (start > end) {
            return null;
        }
        int middle = (int)Math.ceil((start + end) / 2.0);
        if (middle >= elements.size()) {
            return null;
        }

        ScapegoatTreeNode<E> root = new ScapegoatTreeNode<>(elements.get(middle));
        root.size = end - start + 1;
        root.setLeftChild(reBuildCore(elements, start, middle - 1));
        root.setRightChild(reBuildCore(elements, middle + 1, end));
        return root;
    }

    private void clearInsertPath() {
        insertPath.clear();
    }
}

Original starting in www.peihuan.net , please indicate the source

Guess you like

Origin www.cnblogs.com/peihuan/p/11523351.html