做爲一棵二叉搜索樹,那麼最重要的就是如何保持本身的平衡,爲了保持平衡,二叉搜索樹們八仙過海各顯神通,如AVL樹、紅黑樹、Treap樹、伸展樹等等,但萬變不離其宗,他們的方法都是基於旋轉,而後更改節點間的關係。java
尤爲是一些二叉搜索樹實現起來很是很是繁瑣,像紅黑樹,增長和刪除節點總共大約須要處理十來種狀況,寫完debug完估計天都已經黑了幾回了。node
而替罪羊樹就是一棵不同凡響的樹,當碰見不平衡的狀況時,不會想法子調整平衡,直接對她進行暴力重建。segmentfault
上面的這棵子樹,很明顯是不平衡的,雖然暫時不知道基於什麼條件來判斷是否平衡。咱們直接將這棵子樹拍扁,從小到大進行排列(中序遍歷)。ui
將中間的元素當作新的根節點,兩邊的元素分別做爲孩子。這樣對她的重建就完成了,這種感受就好像是從中間拎起來,兩邊耷拉下去同樣。重建後的二叉樹基本爲滿二叉樹,效率極高。this
那麼替罪羊樹又是如何判斷一棵樹是否須要平衡呢。也很是簡單,每棵樹都會取一個平衡因子alpha,範圍是0.5到1之間。假如某棵樹的總節點數 alpha < 某個孩子樹的總結點,那麼就是不平衡的。例如最上圖中,以6爲根節點的子樹一共有7個節點,6的左孩子是以5爲根節點的子樹,一共有5個節點, 假設alpha取 0.7 , 7 0.7 < 5, 所以是不平衡的。spa
對於alpha的取值,若是alpha越小,那麼對平衡的要求更高,重建的次數會更多;alpha越大,樹的平衡程度就會下降,重建的次數也隨之減小。通常而言,alpha取 0.7 比較適中。.net
插入操做開始階段和普通的二叉樹沒有區別,將值插入到合適的葉子節點上後,開始調整平衡。若是自插入的節點從下而上調整,調整完較深層次的子樹後再向上回溯,若是較低層次的樹不知足平衡,全部的子樹仍須要進行重建,那麼有不少重建是無心義的。所以重建都應該從根節點開始,至上向下地判斷是否須要重建。不須要對全部節點進行判斷,只須要判斷從根節點到新插入的葉子節點的路徑中所通過的節點便可。debug
只要發生了一次重建那麼也沒必要再向下遞歸了,所以任意插入一個數,至多發生一次重建。code
刪除有許多種作法:blog
第二種方式和第三種方式區別不大,都是惰刪除,具體使用哪一種方式都行。
暫時只實現了插入操做,刪除操做後續會補完整。
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; } }
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(); } }
首發於 www.peihuan.net,轉載請註明出處