/// <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; }
public void SetSiblings(FHeapNode <T> left, FHeapNode <T> right) { Left = left; Right = right; }