Pairing heap

From Wikipedia, the free encyclopedia
Jump to: navigation, search

A pairing heap is a type of heap data structure with relatively simple implementation and excellent practical amortized performance. However, it has proven very difficult to determine the precise asymptotic running time of pairing heaps.

Pairing heaps are heap ordered multiway trees. Describing the various heap operations is relatively simple (in the following we assume a min-heap):

  • find-min: simply return the top element of the heap.
  • merge: compare the two root elements, the smaller remains the root of the result, the larger element and its subtree is appended as a child of this root.
  • insert: create a new heap for the inserted element and merge into the original heap.
  • decrease-key (optional): remove the subtree rooted at the key to be decreased then merge it with the heap.
  • delete-min: remove the root and merge its subtrees. Various strategies are employed.

The amortized time per delete-min is O(\log n).[1] The operations find-min, merge, and insert are O(1) [2] and decrease-key takes O(2^{2\sqrt{\log\log n}}) amortized time.[3] Fredman proved that the amortized time per decrease-key is at least \Omega(\log\log n).[4]

Although this is worse than other priority queue algorithms such as Fibonacci heaps, which perform decrease-key in O(1) amortized time, the performance in practice is excellent. Stasko and Vitter[5] and Moret and Shapiro[6] conducted experiments on pairing heaps and other heap data structures. They concluded that the pairing heap is as fast as, and often faster than, other efficient data structures like the binary heaps.

Implementation[edit]

A pairing heap is either an empty heap, or a pair consisting of a root element and a possibly empty list of pairing heaps. The heap ordering property requires that all the root elements of the subheaps in the list are not smaller than the root element of the heap. The following description assumes a purely functional heap that does not support the decrease-key operation.

type PairingHeap[Elem] = Empty | Heap(elem: Elem, subheaps: List[PairingHeap[Elem]])

Operations[edit]

find-min[edit]

The function find-min simply returns the root element of the heap:

function find-min(heap)
  if heap == Empty
    error
  else
    return heap.elem

merge[edit]

Merging with an empty heap returns the other heap, otherwise a new heap is returned that has the minimum of the two root elements as its root element and just adds the heap with the larger root to the list of subheaps:

function merge(heap1, heap2)
  if heap1 == Empty
    return heap2
  elsif heap2 == Empty
    return heap1
  elsif heap1.elem < heap2.elem
    return Heap(heap1.elem, heap2 :: heap1.subheaps)
  else
    return Heap(heap2.elem, heap1 :: heap2.subheaps)

insert[edit]

The easiest way to insert an element into a heap is to merge the heap with a new heap containing just this element and an empty list of subheaps:

function insert(elem, heap)
  return merge(Heap(elem, []), heap)

delete-min[edit]

The only non-trivial fundamental operation is the deletion of the minimum element from the heap. The standard strategy first merges the subheaps in pairs (this is the step that gave this datastructure its name) from left to right and then merges the resulting list of heaps from right to left:

function delete-min(heap)
  if heap == Empty
    error
  else
    return merge-pairs(heap.subheaps)

This uses the auxiliary function merge-pairs:

function merge-pairs(l)
  if length(l) == 0
    return Empty
  elsif length(l) == 1
    return l[0]
  else
    return merge(merge(l[0], l[1]), merge-pairs(l[2.. ]))

That this does indeed implement the described two-pass left-to-right then right-to-left merging strategy can be seen from this reduction:

   merge-pairs([H1, H2, H3, H4, H5, H6, H7])
=> merge(merge(H1, H2), merge-pairs([H3, H4, H5, H6, H7]))
     # merge H1 and H2 to H12, then the rest of the list
=> merge(H12, merge(merge(H3, H4), merge-pairs([H5, H6, H7])))
     # merge H3 and H4 to H34, then the rest of the list
=> merge(H12, merge(H34, merge(merge(H5, H6), merge-pairs([H7]))))
     # merge H5 and H6 to H56, then the rest of the list
=> merge(H12, merge(H34, merge(H56, H7)))
     # switch direction, merge the last two resulting heaps, giving H567
=> merge(H12, merge(H34, H567))
     # merge the last two resulting heaps, giving H34567
=> merge(H12, H34567) 
     # finally, merge the first merged pair with the result of merging the rest
=> H1234567

References[edit]

  1. ^ Fredman, Michael L.; Sedgewick, Robert; Sleator, Daniel D.; Tarjan, Robert E. (1986), "The pairing heap: a new form of self-adjusting heap", Algorithmica 1 (1): 111–129, doi:10.1007/BF01840439 .
  2. ^ Iacono, John (2000), "Improved upper bounds for pairing heaps", Proc. 7th Scandinavian Workshop on Algorithm Theory, Lecture Notes in Computer Science 1851, Springer-Verlag, pp. 63–77, doi:10.1007/3-540-44985-X_5, ISBN 978-3-540-67690-4 .
  3. ^ Pettie, Seth (2005), "Towards a final analysis of pairing heaps", Proc. 46th Annual IEEE Symposium on Foundations of Computer Science, pp. 174–183, doi:10.1109/SFCS.2005.75, ISBN 0-7695-2468-0 .
  4. ^ Fredman, Michael L. (1999), "On the efficiency of pairing heaps and related data structures", Journal of the ACM 46 (4): 473–501, doi:10.1145/320211.320214 .
  5. ^ Stasko, John T.; Vitter, Jeffrey S. (1987), "Pairing heaps: experiments and analysis", Communications of the ACM 30 (3): 234–249, doi:10.1145/214748.214759, CiteSeerX: 10.1.1.106.2988 .
  6. ^ Moret, Bernard M. E.; Shapiro, Henry D. (1991), "An empirical analysis of algorithms for constructing a minimum spanning tree", Proc. 2nd Workshop on Algorithms and Data Structures, Lecture Notes in Computer Science 519, Springer-Verlag, pp. 400–411, doi:10.1007/BFb0028279, ISBN 3-540-54343-0, CiteSeerX: 10.1.1.53.5960 .

External links[edit]