Up to this point, I’ve been showing paring heap nodes as having a child list. Conceptually, the node structure looks like this:
class HeapNode public int Data; public HeapNode Children; }
That’s a good conceptual model, but implementing that model can be unwieldy, and can consume a huge amount of memory. When working with small objects in managed languages like .NET, memory allocation overhead can easily exceed the memory used for data. That’s especially true of arrays, which have a 56-byte allocation overhead.
Granted, not all nodes in a pairing heap have children. In fact, at most half of them will. So we can save memory by not allocating an array for the children if there aren’t any. But that adds some complexity to the code, and at best saves only half of the allocation overhead.
Using the .NET
List<T> collection doesn’t help, because
List<T> uses an array as the backing store.
LinkedList<T> will work, but involves its own overhead what with having to manage
In short, managing a per-node list of children can be difficult.
In my introduction to the Pairing heap, I showed this figure:
2 | 8, 7, 3 | | 9 4, 5 | 6
2 is the root of the tree. It has child nodes 8, 7, and 3. 9 is a child of 8. 4 and 5 are children of node 3, and 6 is a child of 4.
More traditionally, that tree would be drawn as:
2 / | \ 8 7 3 / / \ 9 4 5 / 6
That’s the traditional view of the tree. But we can also say that 8 is the first child of 2, 7 is the sibling of 8, and 3 is the sibling of 7. That is, every node has a reference to its first child, and to its next sibling. The node structure, then, is:
class HeapNode public int Data; public HeapNode FirstChild; public HeapNode Sibling; }
As it turns out, there’s a well known binary tree structure called Left-child-right-sibling. Any traditional tree structure can be represented as such a binary tree. Our tree above, when represented as a left-child-right-sibling binary tree, becomes:
2 / 8 / \ 9 7 \ 3 / 4 / \ 6 5
You might notice that this structure bears striking similarity to the original figure from my introduction. It’s the same thing, only rotated 45 degrees clockwise.
As you can see, this builds a very unbalanced binary tree. That’s okay, since we’re not searching it. With pairing heap, all of the action is at the first few tree levels of the tree. A deep tree is good, because it means that we rarely have many siblings to examine when deleting the smallest node.
Implementing a Pairing heap in a left-child-right-sibling binary tree is incredibly easy. Next time I’ll show a simple implementation in C#.