Пример #1
0
        /// <summary>
        /// Applies rebalances
        /// </summary>
        /// <param name="node">Node</param>
        /// <param name="selfSize">Size of the subtree the node represents</param>
        private void Rebalance(IBinaryTreeNode <TKey, TValue> node, long selfSize)
        {
            //Find the scapegoat
            long currSize = selfSize;
            long siblingSize, nodeSize;
            IBinaryTreeNode <TKey, TValue> current = node;

            do
            {
                //Get the sibling subtree size
                siblingSize = current.GetSibling <TKey, TValue>().GetSize <TKey, TValue>();
                if (current.Parent != null)
                {
                    //Total size of the Node is Current size of this subtree plus size of
                    //sibling subtree plus one for the current node
                    nodeSize = currSize + siblingSize + 1;
                    current  = current.Parent;

                    //Is the current node weight balanced?
                    if (currSize <= (this._balanceFactor * nodeSize) && siblingSize <= (this._balanceFactor * siblingSize))
                    {
                        //Weight balanced so continue on
                        currSize = nodeSize;
                    }
                    else
                    {
                        //Not weight balanced so this is the scapegoat we rebalance from
                        break;
                    }
                }
                else
                {
                    //Rebalance at the root is gonna be O(n) for sure
                    break;
                }
            } while (current != null);

            //Check how we need to rebuild after the rebalance
            IBinaryTreeNode <TKey, TValue> parent  = current.Parent;
            bool rebuildLeft = false;

            if (parent != null)
            {
                rebuildLeft = ReferenceEquals(current, parent.LeftChild);
            }

            //Now do a rebalance of the scapegoat which will be whatever current is set to
            IBinaryTreeNode <TKey, TValue>[] nodes = current.Nodes.ToArray();
            foreach (IBinaryTreeNode <TKey, TValue> n in nodes)
            {
                n.Isolate();
            }

            int median = nodes.Length / 2;
            //Console.WriteLine("m = " + median);
            IBinaryTreeNode <TKey, TValue> root = nodes[median];

            root.LeftChild  = this.RebalanceLeftSubtree(nodes, 0, median - 1);
            root.RightChild = this.RebalanceRightSubtree(nodes, median + 1, nodes.Length - 1);

            //Don't use this check because it's expensive, may be useful to turn of for debugging if you ever have issues with the ScapegoatTree
            //if (root.Nodes.Count() != nodes.Length) throw new InvalidOperationException("Scapegoat rebalance lost data, expected " + nodes.Length + " Nodes in rebalanced sub-tree but got " + root.Nodes.Count());

            //Use the rebalanced tree in place of the current node
            if (parent == null)
            {
                //Replace entire tree
                this.Root = root;
            }
            else
            {
                //Replace subtree
                if (rebuildLeft)
                {
                    parent.LeftChild = root;
                }
                else
                {
                    parent.RightChild = root;
                }
            }

            //Reset Max Node code after a rebalance
            this._maxNodeCount = this._nodeCount;
        }