Jump to content

Brodal queue

From Wikipedia, the free encyclopedia
Brodal queue
TypeHeap/priority queue
Invented1996
Invented byGerth Stølting Brodal
Time complexity in big O notation
Operation Average Worst case
Space complexity

In computer science, the Brodal queue is a heap/priority queue structure with very low worst case time bounds: for insertion, find-minimum, meld (merge two queues) and decrease-key and for delete-minimum and general deletion. They are the first heap variant to achieve these bounds without resorting to amortization of operational costs. Brodal queues are named after their inventor Gerth Stølting Brodal.[1]

While having better asymptotic bounds than other priority queue structures, they are, in the words of Brodal himself, "quite complicated" and "[not] applicable in practice."[1] Brodal and Okasaki describe a persistent (purely functional) version of Brodal queues.[2]

Definition

[edit]

A Brodal queue is a set of two trees and and 5 guides. The definition of the guide data structure can be found in the following section. For both trees, each node has a rank, this rank is useful for later operations and intuitively corresponds to the logarithm of the size of the subtree rooted in the node. We note the number of children of the node with rank . We will also use for the root of tree and for the root of tree . At each given time, every subtree rooted in a node needs to fulfill these 5 invariants (which will be later called invariants):

  •  : If is a leaf, then ,
  •  : ,
  •  : If , then ,
  • : we highlight that ,
  •  : or .

Here guarantees us that the size of the subtree rooted in a node is at least exponential to the rank of that node. In addition, bounds the number of children of each rank for a given node, this implies that all nodes have rank and degrees in .

In a Brodal queue, not every node will have a bigger value than its parent, the nodes vialoating this condition will be called violating nodes. However, we want to keep the number of violating nodes relatively small. To keep track of violating nodes, we create for each node two sets and of nodes larger than . Intuitively, are the nodes larger of with large rank (such that if ), and are the nodes with small rank (). These sets are implemented using doubly linked list meaning they have an order. In particular, all violating nodes added to are added at the front of the list, and all violating nodes added to are inserted next to a node of same rank. We let denote the number of nodes in of rank The and lists fulfill these 5 invariants (we will call the invariants):

  •  :
  •  : If then
  • : If then there exist a node such that
  • :
  • : By denoting , we have: for a certain constant .

Since all nodes have rank in the and , all and are in size .

We also have some invariants of the roots of the trees and : and (called the invariants).

  •  : ,
  • : ,
  • : if , then .

The invariant essentially tells us that if we increase the rank of by one, we have at most new "large" violations (here large means having a high rank) without violating the invariant. On the other hand the invariant tells us that all violations in are "small", this invariant is true per the definition of . Maintaining the invariants and is not trivial, to maintain these we will use the operation which can be implemented using a guide as defined in the next section. Each time we will call the operation, we will essentially :

  1. Add the new violation to or depending on the rank of that violation.
  2. To avoid and from getting too large, we incrementally do two kinds of transformations:
    1. Moving the sons of to to increase the rank of
    2. Reducing the number of violations in by replacing two violations of rank to one violation of rank

The guide data structure

[edit]

This definition is based on the definition from Brodal's paper.[3]

We assume that we have a sequence of variables and we want to make sure that for some threshold . The only operation allowed is which decreases by at least 2 and increases by at most 1. We can assume without loss of generality that reduces by 2 and increases by 1.

If a is increased by one, the goal of the guide is to tell us on which indices to apply in order to respect the threshold. The guide is only allowed to make calls to the function for each increase.

The guide has access to another sequence such that and . As long as after the increase of we have we do not need to ask help from our guide since is "far" bellow . However, if before the increase, then we have after the change.

To simplify the explanation, we can assume that , so that . The guide will create blocks in the sequence of of form where we allow there to be no . The guide maintains the invariant that each element which isn't in a block is either a or a . For example, here are the blocks for a sequence of .

The guide is composed of 3 arrays :

  • the array of
  • the array of
  • an array of pointers where all the for which are in the same block will point to the same memory cell containing a value. If a is not in a block, then points to a memory cell containing .

With this definition, a guide has two important properties :

  1. For each element in a block, we can find the most left element of the block in time .
  2. We can destroy a block in time by assigning to the memory cell pointed to by each element of the block.

This way, the guide is able to decide which indices to in time . Here is an example :

To reestablish the blocks, the pointers of the 1 and 0 added to the first block now point to the same cell as all the other elements from the first block, and the value of the second block's cell is changed to . In the previous example, only two operations were needed, this is actually the case for all instances. Therefore, the queue only needs operations to reestablish the property.

Operations of a Brodal Queue

[edit]

To implement the different priority queue operations we first need to describe some essential transformations for the trees.

Transformations

[edit]

Linking trees

[edit]

To link trees, we need three nodes of equal rank. We can calculate the minimum of these three nodes with two comparisons. Here we assume that is the minimum but the process is similar for all . We can now make the nodes and the two leftmost sons of and increase the rank of by one. This preserves all the and invariants.

Delinking trees

[edit]

If has exactly two or three sons of rank , we can remove these sons and gets the rank of its new largest son plus one. From the condition, we know that that the invariant will be preserved. Then, all the and invariants remain satisfied. If has 4 or more children, we can simply cut of two of them and all the invariants remain true. Therefore, the delinking of a tree of rank will always result in two or three trees of rank (from the 2 or 3 children cut off) and one additional tree of rank at most .

Maintaining the sons of a root

[edit]

When we add and remove sons of a root, we want to keep the invariant true. For this purpose, we use 4 guides, two for each roots and . To have constant time access to the son of we create an extendible array of pointed that has for each rank a pointer to a son of of rank . One guide will maintain the condition that and the other maintains both for . The sons of of rank and are treated separately in a straight forward way to maintant their number between 2 and 7. The equivalent to the variable in the definition of the guide will have the values for the higher bound guide and for the lower bound.

In this context, when we add a child of rank to the root, we increase by one, and apply the operations. The operation here consists of linking three trees of rank which creates a new child of rank . Therefore, we decrease by three and increase by one. If this increase results in too many sons of rank or we link some of these sons together and possibly increase the rank of . If we increase the rank of , we have to increase the length of the extendible array managed by the guides.

Cutting off a son from is very similar, except here the operation corresponds to the delinking of a tree.

For the root the situation is nearly the same. However, since guarantees us that is the minimum element, we know that we won't create any violation by linking or delinking children of . The same cannot be said for . Linking sons will never create new violations but the delinking of sons can create up to three new violations. The tree left over by a delinking is made a son of if it has rank less than and otherwise it becomes a son of . The new violations which have rank larger than are added to . To maintain the invariants on the set (namely and ), we have to guarantee that the rank of will be increased and that in those invariants is chosen large enough.

Violation reducing

[edit]

The goal of this transformation is to reduce the total amount of potential violations, meaning reducing .

We assume we have two potential violations and of equal rank . We then have several cases:

  1. If one of the nodes turns out not to be a violation, we just remove it from its corresponding violation set.
  2. Otherwise, both nodes are violations. Because of , we know that both and have at least one brother. Then:
    1. If and are not brothers, then we can assume without losing generality that , we can then swap the subtrees rooted in and . The number of violations can only go down during that swap.
    2. Else, and are brothers of a node we will call .
      1. If has more than one brother of rank , we can just cut off and make it a non violating node of as described in the previous subsection.
      2. Else, and are the only children of rank of ..
        1. If , we can cut off both and nodes from and make them non violating nodes of as described in the previous subsection
        2. Else, . We will cut off , the new rank of will be one plus the rank of its leftmost son, We replace by a son on of rank , which can be cut off as described in the previous subsection. If the replacement for becomes a violating node of rank , we add it to . Finally we make new sons of as described above.

Avoiding too many violations

[edit]

The only violation sets where we will add violations are and . As described above, the invariants on those sets are maintained using guides. When we add a violation to we have two cases:

  1. If there are exactly 6 violations of the given rank and there are at least two violating nodes which aren't sons of , we apply the operations given by the guide.
  2. If there are more than 4 violations which are sons of , we cut the additional violations off and link them below . This removes the violation created by these nodes and doesn't affect the guide maintaining the sons of .

For each priority queue operation that is performed, we increase the rank of by at least one by moving a constant number of sons of to (provided that ). Increasing the rank of allows us to add violations to while still maintaining all our invariants. If and we can cut the largest sons of , link them to and then make a son of . This satisfies all the invariants. Otherwise, we cut off a son of of rank , delink this son and add the resulting trees to . If , we know that is the node of largest rank, therefore we know that no large violations can be created.

Priority queue operations

[edit]

MakeQueue

[edit]

just returns a pair of empty trees.

FindMin

[edit]

returns .

Insert

[edit]

is only a special case of where is a queue only containing and .

Meld

[edit]

involves four trees (two for each queue). The tree having the minimum root becomes the new tree. If this tree is also the tree of maximum rank, we can add all the other trees below as described previously. In this case, no violating node is created so no transformation is done on violating nodes. Otherwise, the tree of maximum rank becomes the new tree and the other trees are added below as described in the section "Maintaining the sons of a root". If some trees have equal rank to this new , we can delink them before adding them. The violations created are handled as explained in the section : "Avoiding too many violations".

DecreaseKey

[edit]

replaces the element of by (with ). If , we swap the two nodes, otherwise, we handle the potential new violation as explained in the section "Avoiding too many violations".

DeleteMin

[edit]

is allowed to take worst case time . First, we completely empty by moving all the sons of to then making a rank 0 son of . Then, is deleted, this leaves us with at most independant trees. The new minimum is then found by looking at the violating sets of the old root and looking at all the roots of the new trees. If the minimum element is not a root, we can swap a root from a tree of equal rank to it. This creates at most one violation. Then, we make the independant trees sons of the new minimum element by performing linking and delinking operations. This reestablishes the and invariants. By merging the and sets of the new root along with the and sets of the old root together, we get one new violation set of size . By doign at most violation reducing tranformations we can make the violation set only contain at most one element of each rank. This set will be our new set and the new set is empty. This reestablishes the invariants. We also have to initialise a new guide for the new root .

Delete

[edit]

Here, denotes the smallest possible element. can simply be implemented by calling followed by .

Implementation details

[edit]

In this section, we summarize some implementation details for the Brodal Queue data structure.

In each tree, each node is a record having the following fields:

  • The element associated with the node (its value),
  • The rank of the node,
  • pointers to the node's left and right brothers,
  • a pointer to the father node,
  • a pointer to the leftmost son,
  • pointers to the first element of the node's and sets,
  • pointers to the following and previous element in the violation set the node belongs to. If this node is the first node of the violation set or it belongs to, the previous pointer points to .
  • an array of pointers to sons of of rank (with ),
  • a similar array for ,
  • an array of pointers to nodes in of rank (with ).

Finally, we have 5 guides: three to maintain the upper bounds on , and and two to maintain the lower bounds on and .

Due to its high amount of pointers and sets to keep track of, the Brodal Queue is extremely hard to implement. For this reason it is best described as a purely theoretical object to lower time complexity on algorithms like Dijkstra's algorithm. However, the Brodal has been implemented in Scala (the github repository can be found here: https://github.com/ruippeixotog/functional-brodal-queues). In his paper, Gerth Stølting Brodal mentions that: "An important issue for further work is to simplify the construction to make it applicable in practice"[3].

Summary of running times

[edit]

Here are time complexities[4] of various heap data structures. The abbreviation am. indicates that the given complexity is amortized, otherwise it is a worst-case complexity. For the meaning of "O(f)" and "Θ(f)" see Big O notation. Names of operations assume a min-heap.

Operation find-min delete-min decrease-key insert meld make-heap[a]
Binary[4] Θ(1) Θ(log n) Θ(log n) Θ(log n) Θ(n) Θ(n)
Skew[5] Θ(1) O(log n) am. O(log n) am. O(log n) am. O(log n) am. Θ(n) am.
Leftist[6] Θ(1) Θ(log n) Θ(log n) Θ(log n) Θ(log n) Θ(n)
Binomial[4][8] Θ(1) Θ(log n) Θ(log n) Θ(1) am. Θ(log n)[b] Θ(n)
Skew binomial[9] Θ(1) Θ(log n) Θ(log n) Θ(1) Θ(log n)[b] Θ(n)
2–3 heap[11] Θ(1) O(log n) am. Θ(1) Θ(1) am. O(log n)[b] Θ(n)
Bottom-up skew[5] Θ(1) O(log n) am. O(log n) am. Θ(1) am. Θ(1) am. Θ(n) am.
Pairing[12] Θ(1) O(log n) am. o(log n) am.[c] Θ(1) Θ(1) Θ(n)
Rank-pairing[15] Θ(1) O(log n) am. Θ(1) am. Θ(1) Θ(1) Θ(n)
Fibonacci[4][16] Θ(1) O(log n) am. Θ(1) am. Θ(1) Θ(1) Θ(n)
Strict Fibonacci[17][d] Θ(1) Θ(log n) Θ(1) Θ(1) Θ(1) Θ(n)
Brodal[18][d] Θ(1) Θ(log n) Θ(1) Θ(1) Θ(1) Θ(n)[19]
  1. ^ make-heap is the operation of building a heap from a sequence of n unsorted elements. It can be done in Θ(n) time whenever meld runs in O(log n) time (where both complexities can be amortized).[5][6] Another algorithm achieves Θ(n) for binary heaps.[7]
  2. ^ a b c For persistent heaps (not supporting decrease-key), a generic transformation reduces the cost of meld to that of insert, while the new cost of delete-min is the sum of the old costs of delete-min and meld.[10] Here, it makes meld run in Θ(1) time (amortized, if the cost of insert is) while delete-min still runs in O(log n). Applied to skew binomial heaps, it yields Brodal-Okasaki queues, persistent heaps with optimal worst-case complexities.[9]
  3. ^ Lower bound of [13] upper bound of [14]
  4. ^ a b Brodal queues and strict Fibonacci heaps achieve optimal worst-case complexities for heaps. They were first described as imperative data structures. The Brodal-Okasaki queue is a persistent data structure achieving the same optimum, except that decrease-key is not supported.

Gerth Stølting Brodal

[edit]

Gerth Stølting Brodal is a professor at the University of Aarhus, Denmark.[20] He is best known for the Brodal queue.

References

[edit]
  1. ^ a b Gerth Stølting Brodal (1996). Worst-case efficient priority queues. Proc. 7th ACM-SIAM Symposium on Discrete Algorithms, pp. 52–58
  2. ^ Gerth Stølting Brodal and Chris Okasaki (1996). Optimal purely functional priority queues. Journal of Functional Programming.
  3. ^ a b Brodal, Gerth Stølting (1996). "Worst-Case Efficient Priority Queues" (PDF).{{cite web}}: CS1 maint: url-status (link)
  4. ^ a b c d Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L. (1990). Introduction to Algorithms (1st ed.). MIT Press and McGraw-Hill. ISBN 0-262-03141-8.
  5. ^ a b c Sleator, Daniel Dominic; Tarjan, Robert Endre (February 1986). "Self-Adjusting Heaps". SIAM Journal on Computing. 15 (1): 52–69. CiteSeerX 10.1.1.93.6678. doi:10.1137/0215004. ISSN 0097-5397.
  6. ^ a b Tarjan, Robert (1983). "3.3. Leftist heaps". Data Structures and Network Algorithms. pp. 38–42. doi:10.1137/1.9781611970265. ISBN 978-0-89871-187-5.
  7. ^ Hayward, Ryan; McDiarmid, Colin (1991). "Average Case Analysis of Heap Building by Repeated Insertion" (PDF). J. Algorithms. 12: 126–153. CiteSeerX 10.1.1.353.7888. doi:10.1016/0196-6774(91)90027-v. Archived from the original (PDF) on 2016-02-05. Retrieved 2016-01-28.
  8. ^ "Binomial Heap | Brilliant Math & Science Wiki". brilliant.org. Retrieved 2019-09-30.
  9. ^ a b Brodal, Gerth Stølting; Okasaki, Chris (November 1996), "Optimal purely functional priority queues", Journal of Functional Programming, 6 (6): 839–857, doi:10.1017/s095679680000201x
  10. ^ Okasaki, Chris (1998). "10.2. Structural Abstraction". Purely Functional Data Structures (1st ed.). pp. 158–162. ISBN 9780521631242.
  11. ^ Takaoka, Tadao (1999), Theory of 2–3 Heaps (PDF), p. 12
  12. ^ Iacono, John (2000), "Improved upper bounds for pairing heaps", Proc. 7th Scandinavian Workshop on Algorithm Theory (PDF), Lecture Notes in Computer Science, vol. 1851, Springer-Verlag, pp. 63–77, arXiv:1110.4428, CiteSeerX 10.1.1.748.7812, doi:10.1007/3-540-44985-X_5, ISBN 3-540-67690-2
  13. ^ Fredman, Michael Lawrence (July 1999). "On the Efficiency of Pairing Heaps and Related Data Structures" (PDF). Journal of the Association for Computing Machinery. 46 (4): 473–501. doi:10.1145/320211.320214.
  14. ^ Pettie, Seth (2005). Towards a Final Analysis of Pairing Heaps (PDF). FOCS '05 Proceedings of the 46th Annual IEEE Symposium on Foundations of Computer Science. pp. 174–183. CiteSeerX 10.1.1.549.471. doi:10.1109/SFCS.2005.75. ISBN 0-7695-2468-0.
  15. ^ Haeupler, Bernhard; Sen, Siddhartha; Tarjan, Robert E. (November 2011). "Rank-pairing heaps" (PDF). SIAM J. Computing. 40 (6): 1463–1485. doi:10.1137/100785351.
  16. ^ Fredman, Michael Lawrence; Tarjan, Robert E. (July 1987). "Fibonacci heaps and their uses in improved network optimization algorithms" (PDF). Journal of the Association for Computing Machinery. 34 (3): 596–615. CiteSeerX 10.1.1.309.8927. doi:10.1145/28869.28874.
  17. ^ Brodal, Gerth Stølting; Lagogiannis, George; Tarjan, Robert E. (2012). Strict Fibonacci heaps (PDF). Proceedings of the 44th symposium on Theory of Computing - STOC '12. pp. 1177–1184. CiteSeerX 10.1.1.233.1740. doi:10.1145/2213977.2214082. ISBN 978-1-4503-1245-5.
  18. ^ Brodal, Gerth S. (1996), "Worst-Case Efficient Priority Queues" (PDF), Proc. 7th Annual ACM-SIAM Symposium on Discrete Algorithms, pp. 52–58
  19. ^ Goodrich, Michael T.; Tamassia, Roberto (2004). "7.3.6. Bottom-Up Heap Construction". Data Structures and Algorithms in Java (3rd ed.). pp. 338–341. ISBN 0-471-46983-1.
  20. ^ "Website of Gerth Stølting Brodal, at the University of Aarhus". Retrieved 18 February 2016.