/// <summary>
        /// Remove x from the child list of y.
        /// </summary>
        /// <param name="x">A child of y we just decreased the value of.</param>
        /// <param name="y">The now former parent of x.</param>
        protected void Cut(FHeapNode <T> x, FHeapNode <T> y)
        {
            if (MinItem == null)
            {
                throw new InvalidOperationException("Heap malformed");
            }

            if (y.Degree == 1)
            {
                y.Child = null;
                MinItem.AddRight(x);
            }
            else if (y.Degree > 1)
            {
                x.Remove();
            }
            else
            {
                throw new InvalidOperationException("Heap malformed");
            }

            y.Degree--;
            x.Mark   = false;
            x.Parent = null;
        }
 /// <summary>
 /// A helper function to add a node to the right of this one in the current
 /// circularly doubly linked list.
 /// </summary>
 /// <param name="node">A node to go in the linked list.</param>
 public void AddRight(FHeapNode <T> node)
 {
     Right.Left = node;
     node.Right = Right;
     node.Left  = this;
     Right      = node;
 }
        /// <summary>
        /// Reduce the key of x to be k.
        /// </summary>
        /// <remarks>
        /// k must be less than x.Key, increasing the key of an item is not supported.
        /// </remarks>
        /// <param name="x">The item you want to reduce in value.</param>
        /// <param name="k">The new value for the item.</param>
        public void DecreaseKey(FHeapNode <T> x, T k)
        {
            if (MinItem == null)
            {
                throw new ArgumentException($"{nameof(x)} is not from the heap");
            }

            if (x.Key == null)
            {
                throw new ArgumentException("x has no value");
            }

            if (k.CompareTo(x.Key) > 0)
            {
                throw new InvalidOperationException("Value cannot be increased");
            }

            x.Key = k;
            var y = x.Parent;

            if (y != null && x.Key.CompareTo(y.Key) < 0)
            {
                Cut(x, y);
                CascadingCut(y);
            }

            if (x.Key.CompareTo(MinItem.Key) < 0)
            {
                MinItem = x;
            }
        }
        /// <summary>
        /// Combine the linked list that <c>otherList</c> sits inside, with the
        /// linked list this is in. Do this by cutting the link between this node,
        /// and the node to the right of this, and inserting the contents of the
        /// otherList in between.
        /// </summary>
        /// <param name="otherList">A node from another list whose elements we want
        /// to concatenate to this list.</param>
        public void ConcatenateRight(FHeapNode <T> otherList)
        {
            Right.Left           = otherList.Left;
            otherList.Left.Right = Right;

            Right          = otherList;
            otherList.Left = this;
        }
        /// <summary>
        /// <para>
        /// Consolidate is analogous to Heapify in <see cref="DataStructures.Heap.BinaryHeap{T}"/>.
        /// </para>
        /// <para>
        /// First, an array <c>A</c> [0...D(H.n)] is created where H.n is the number
        /// of items in this heap, and D(x) is the max degree any node can have in a
        /// Fibonacci heap with x nodes.
        /// </para>
        /// <para>
        /// For each node <c>x</c> in the root list, try to add it to <c>A[d]</c> where
        /// d is the degree of <c>x</c>.
        /// If there is already a node in <c>A[d]</c>, call it <c>y</c>, and make
        /// <c>y</c> a child of <c>x</c>. (Swap <c>x</c> and <c>y</c> beforehand if
        /// <c>x</c> is greater than <c>y</c>). Now that <c>x</c> has one more child,
        /// remove if from <c>A[d]</c> and add it to <c>A[d+1]</c> to reflect that its
        /// degree is one more. Loop this behavior until we find an empty spot to put
        /// <c>x</c>.
        /// </para>
        /// <para>
        /// With <c>A</c> all filled, empty the root list of the heap. And add each item
        /// from <c>A</c> into the root list, one by one, making sure to keep track of
        /// which is smallest.
        /// </para>
        /// </summary>
        protected void Consolidate()
        {
            if (MinItem == null)
            {
                return;
            }

            // There's a fact in Intro to Algorithms:
            // "the max degree of any node in an n-node fibonacci heap is O(lg(n)).
            // In particular, we shall show that D(n) <= floor(log_phi(n)) where phi is
            // the golden ratio, defined in equation 3.24 as phi = (1 + sqrt(5))/2"
            //
            // For a proof, see [1]
            var maxDegree = 1 + (int)Math.Log(Count, (1 + Math.Sqrt(5)) / 2);

            // Create slots for every possible node degree of x
            var a        = new FHeapNode <T>?[maxDegree];
            var siblings = SiblingIterator(MinItem).ToList();

            foreach (var w in siblings)
            {
                var x = w;
                var d = x.Degree;

                var y = a[d];

                // While A[d] is not empty, we can't blindly put x here
                while (y != null)
                {
                    if (x.Key.CompareTo(y.Key) > 0)
                    {
                        // Exchange x and y
                        var temp = x;
                        x = y;
                        y = temp;
                    }

                    // Make y a child of x
                    FibHeapLink(y, x);

                    // Empty out this spot since x now has a higher degree
                    a[d] = null;

                    // Add 1 to x's degree before going back into the loop
                    d++;

                    y = a[d];
                }

                // Now that there's an empty spot for x, place it there
                a[d] = x;
            }

            ReconstructHeap(a);
        }
        /// <summary>
        /// A helper function to iterate through all the siblings of this node in the
        /// circularly doubly linked list.
        /// </summary>
        /// <param name="node">A node we want the siblings of.</param>
        /// <returns>An iterator over all of the siblings.</returns>
        protected IEnumerable <FHeapNode <T> > SiblingIterator(FHeapNode <T> node)
        {
            var currentNode = node;

            yield return(currentNode);

            currentNode = node.Right;
            while (currentNode != node)
            {
                yield return(currentNode);

                currentNode = currentNode.Right;
            }
        }
        /// <summary>
        /// Similar to AddRight, but adds the node as a sibling to the child node.
        /// </summary>
        /// <param name="node">A node to add to the child list of this node.</param>
        public void AddChild(FHeapNode <T> node)
        {
            Degree++;

            if (Child == null)
            {
                Child        = node;
                Child.Parent = this;
                Child.Left   = Child.Right = Child;

                return;
            }

            Child.AddRight(node);
        }
        /// <summary>
        /// Rebalances the heap after the decrease operation takes place.
        /// </summary>
        /// <param name="y">An item that may no longer obey the heap property.</param>
        protected void CascadingCut(FHeapNode <T> y)
        {
            var z = y.Parent;

            if (z != null)
            {
                if (!y.Mark)
                {
                    y.Mark = true;
                }
                else
                {
                    Cut(y, z);
                    CascadingCut(z);
                }
            }
        }
        /// <summary>
        /// Return the MinItem and remove it from the heap.
        /// </summary>
        /// <remarks>
        /// This function (with all of its helper functions) is the most complicated
        /// part of the Fibonacci Heap. However, it can be broken down into a few steps.
        /// <list type="number">
        /// <item>
        /// Add the children of MinItem to the root list. Either one of these children,
        /// or another of the items in the root list is a candidate to become the new
        /// MinItem.
        /// </item>
        /// <item>
        /// Remove the MinItem from the root list and appoint a new MinItem temporarily.
        /// </item>
        /// <item>
        /// <see cref="DataStructures.Heap.FibonacciHeap{T}.Consolidate"/> what's left
        /// of the heap.
        /// </item>
        /// </list>
        /// </remarks>
        /// <returns>The minimum item from the heap.</returns>
        public T Pop()
        {
            FHeapNode <T>?z = null;

            if (MinItem == null)
            {
                throw new InvalidOperationException("Heap is empty!");
            }

            z = MinItem;

            // Since z is leaving the heap, add its children to the root list
            if (z.Child != null)
            {
                foreach (var x in SiblingIterator(z.Child))
                {
                    x.Parent = null;
                }

                // This effectively adds each child x to the root list
                z.ConcatenateRight(z.Child);
            }

            if (Count == 1)
            {
                MinItem = null;
                Count   = 0;
                return(z.Key);
            }

            // Temporarily reassign MinItem to an arbitrary item in the root
            // list
            MinItem = MinItem.Right;

            // Remove the old MinItem from the root list altogether
            z.Remove();

            // Consolidate the heap
            Consolidate();

            Count -= 1;

            return(z.Key);
        }
        /// <summary>
        /// Add item <c>x</c> to this Fibonacci heap.
        /// </summary>
        /// <remarks>
        /// To add an item to a Fibonacci heap, we simply add it to the "root list",
        /// a circularly doubly linked list where our minimum item sits. Since adding
        /// items to a linked list takes O(1) time, the overall time to perform this
        /// operation is O(1).
        /// </remarks>
        /// <param name="x">An item to push onto the heap.</param>
        /// <returns>A reference to the item as it is in the heap. This is used for
        /// operations like decresing key.</returns>
        public FHeapNode <T> Push(T x)
        {
            Count++;

            var newItem = new FHeapNode <T>(x);

            if (MinItem == null)
            {
                MinItem = newItem;
            }
            else
            {
                MinItem.AddRight(newItem);

                if (newItem.Key.CompareTo(MinItem.Key) < 0)
                {
                    MinItem = newItem;
                }
            }

            return(newItem);
        }
        /// <summary>
        /// Reconstructs the heap based on the array of node degrees created by the consolidate step.
        /// </summary>
        /// <param name="a">An array of FHeapNodes where a[i] represents a node of degree i.</param>
        protected void ReconstructHeap(FHeapNode <T>?[] a)
        {
            // Once all items are in A, empty out the root list
            MinItem = null;

            for (int i = 0; i < a.Length; i++)
            {
                var r = a[i];
                if (r == null)
                {
                    continue;
                }

                if (MinItem == null)
                {
                    // If the root list is completely empty, make this the new
                    // MinItem
                    MinItem = r;

                    // Make a new root list with just this item. Make sure to make
                    // it its own list.
                    MinItem.SetSiblings(MinItem, MinItem);
                    MinItem.Parent = null;
                }
                else
                {
                    // Add A[i] to the root list
                    MinItem.AddRight(r);

                    // If this item is smaller, make it the new min item
                    if (MinItem.Key.CompareTo(r.Key) > 0)
                    {
                        MinItem = a[i];
                    }
                }
            }
        }
        /// <summary>
        /// Combines all the elements of two fibonacci heaps.
        /// </summary>
        /// <remarks>
        /// To union two Fibonacci heaps is a single fibonacci heap is a single heap
        /// that contains all the elements of both heaps. This can be done in O(1) time
        /// by concatenating the root lists together.
        ///
        /// For more details on how two circularly linked lists are concatenated, see
        /// <see cref="DataStructures.Heap.FHeapNode{T}.ConcatenateRight(DataStructures.Heap.FHeapNode{T})"/>
        ///
        /// Finally, check to see which of <c>this.MinItem</c> and <c>other.MinItem</c>
        /// is smaller, and set <c>this.MinItem</c> accordingly
        ///
        /// This operation destroys <c>other</c>.
        /// </remarks>
        /// <param name="other">Another heap whose elements we wish to add to this heap.
        /// The other heap will be destroyed as a result.</param>
        public void Union(FibonacciHeap <T> other)
        {
            // If there are no items in the other heap, then there is nothing to do.
            if (other.MinItem == null)
            {
                return;
            }

            // If this heap is empty, simply set it equal to the other heap
            if (MinItem == null)
            {
                // Set this heap to the other one
                MinItem = other.MinItem;
                Count   = other.Count;

                // Destroy the other heap
                other.MinItem = null;
                other.Count   = 0;

                return;
            }

            Count += other.Count;

            // <see cref="DataStructures.FibonacciHeap{T}.FHeapNode.ConcatenateRight(DataStructures.FibonacciHeap{T}.FHeapNode)"/>
            MinItem.ConcatenateRight(other.MinItem);

            // Set the MinItem to the smaller of the two MinItems
            if (other.MinItem.Key.CompareTo(MinItem.Key) < 0)
            {
                MinItem = other.MinItem;
            }

            other.MinItem = null;
            other.Count   = 0;
        }
 /// <summary>
 /// Make y a child of x.
 /// </summary>
 /// <param name="y">A node to become the child of x.</param>
 /// <param name="x">A node to become the parent of y.</param>
 protected void FibHeapLink(FHeapNode <T> y, FHeapNode <T> x)
 {
     y.Remove();
     x.AddChild(y);
     y.Mark = false;
 }
Exemple #14
0
 public void SetSiblings(FHeapNode <T> left, FHeapNode <T> right)
 {
     Left  = left;
     Right = right;
 }