Efficient Priority Queues Presenter: Yulik Feldman Based on works of P.Emde Boas and D.Knuth 1 Agenda Background: the definition of priority queue. The standard implementation of priority queue: space O(n), time O(log n). Working with integers: more efficient implementation is possible. The draft program: space O(n), time is O(log log n) for most cases, but worst case is still O(log n). The revised program: space O(n), time O(log log n). 2 Priority Queue Definition Represents a subset of a given set of elements and provides the following operations on it: Size() – number of elements in the subset Min() – the smallest element in the subset Max() – the largest element in the subset Insert(X) – insert X into the subset Delete(X) – delete X from the subset 3 Priority Queue Definition Successor(X) – the smallest element in the subset that is greater than X Predecessor(X) – the largest element in the subset that is less than X Some custom definitions also include a number of other operations, such as Empty(), Member(X), ExtractMin(), ExtractMax(). 4 Standard Implementation Usually, the priority queues are implemented using some kind of a balanced tree, like AVL, 2-3 tree or binary heap. These representations require O(n) space and O(log n) time for the Insert, Delete, Successor and Predecessor operations. The Size, Min and Max are trivially done in O(1) time. 5 Limitations of the standard approach The standard approach suffers from the theoretical lower bound of O(log n) processing time for the decision tree model of order n. However, if the universe is assumed to consist of the integers {1, …, n} only, this bound can be outwitted. For example, it is known that n integers in range {1, …, n} can be sorted in linear time. 6 Taking advantage of integers Assuming that the universe consists of the integers in range {1,…,n}, let n = n1 * n2. Define q(x) = x / n2, and r(x) = x mod n2. We can build a representation from n1 priority queues of order n2, plus an additional priority queue of order n1 to keep track of which of the others are nonempty. Thus the universe is made up of n1 “galaxies” of smaller universes. 7 The recursive construction The universe: a single PQ of order n1 The galaxies: n1 PQs of order n2 Each galaxy is subsequently split to sub-galaxies 8 Implementation: data members Class CPriorityQueue { int nSize; // size of the subset int nLeast; // the smallest element int nGreatest; // the largest element PriorityQueue* pT; // a queue of order n1 for the nonempty galaxies PriorityQueue* pG[n1-1]; // n1 queues of order n2 for the galaxies … Functions follow … } 9 Initialization and trivial functions CPriorityQueue(int nOrder) { nSize = 0; nLeast = MAXINT; nGreatest = MININT; pT = new CPriorityQueue(n1); for (I = 0; I < n1; I++) pG[I] = new CPriorityQueue(n2); } Size() { return nSize; } Min() { return nLeast; } Max() { return nGreatest; } 10 Insertion Insert(int X) { nSize++; if (X < nLeast) nLeast = X; if (X > nGreatest) nGreatest = X; if (pG[q(X)]->Size() == 0) pT->Insert(q(x)); pG[q(X)]->Insert(r(X)); } 11 Deletion Delete(int X) { nSize--; pG[q(X)]->Delete(r(X)); if (pG[q(X)]->nSize == 0) pT->Delete(q(X)); if (nSize == 0) { nLeast = MAXINT; nGreatest = MININT; } else { nLeast = pG[pT->Min()]->Min() + n2 * pT->Min(); nGreatest = pG[pT->Min()]->Max() + n2 * pT->Max(); } } 12 Finding successor/predecessor Successor(int X) { if (r(X) > pG[q(X)]->Max()) return (pG[pT->Successor(q(X))]->Min() + n2 * pT->Successor(q(X))) else return (pG[q(X)]->Successor(r(X)) + n2 * q(x)); } 13 Time complexity The time for Size(), Min() and Max() is O(1). For Insert(), T(n) = O(1) + T(n2) + (if trivial n2 insertion then T(n1)) For Delete(), T(n) = O(1) + T(n2) + (if trivial n2 deletion then T(n1)) For Successor(), T(n) <= O(1) + max(T(n1), T(n2)) 14 Time complexity (cont.) We get best performance by taking n1 = n2 = sqrt(n). The solution to the recurrence T(n) = 2T(sqrt(n)) + O(1) has T(n) = log n, while the solution to T(n) = T(sqrt(n)) + O(1) has T(n) = log log n, hence for best asymptotic growth we should try to improve the algorithm. 15 Improving the algorithm We should ensure that the trivial insertions and deletions always take O(1) time. The tricky thing is that trivial deletions don’t have time to clean up the data structure, and trivial insertions haven’t the time to reinitialize it. We solve it in the following way: whenever nSize <= 1, the galaxies are not used and all subsidiary queues will be empty. 16 The revised insertion Everything is the same as before, except that the implementation of Insert(), Delete() and Successor() is changed as follows: Insert(int X) { if (nSize == 0) { nSize=1; nLeast = X; nGreatest = X; } else { … nSize = 0 optimization 17 The revised insertion if (nSize == 1) { pT->Insert(q(nLeast)); pG[q(nLeast)]->Insert(r(nLeast))); } if (pG[q(X)]->Size() == 0) pT->Insert(q(x)); pG[q(X)]->Insert(r(X)); nSize++; if (X < nLeast) nLeast = X; if (X > nGreatest) nGreatest = X; } nSize = 1 optimization Original algorithm } 18 The revised deletion Delete(int X) { nSize--; if (nSize == 0) { nLeast = MAXINT; nGreatest = MININT; } else { pG[q(X)]->Delete(r(X)); if (pG[q(X)]->Size() == 0) T->Delete(q(X)); … nSize = 0 optimization Original algorithm 19 The revised deletion if (X == nLeast) nLeast = pG[pT->Min()]->Min() + n2 * T->Min(); else if (X == nGreatest) nGreatest = pG[pT->Max()]->Max() + n2 * T->Max(); if (nSize == 1) { pT->Delete(q(nLeast)); pG[q(nLeast)]->Delete(r(nLeast))); } Original algorithm nSize = 1 optimization } 20 The revised successor Successor(int X) { if (nSize <= 1) nSize <= 1 return nGreatest; optimization else if (r(X) > pG[q(X)]->Max()) return (pG[pT->Successor(q(X))]->Min() + n2 * pT->Successor(q(X))) Original else algorithm return (pG[q(X)]->Successor(r(X)) + n2 * q(x)); } 21 The limits of the real universe The recursive construction will never go many levels before exceeding the size of the real universe. For example, if we have a 32-bit machine and use built-in operations on the lowest level of size 32, the next level takes N up to 32*32 = 2^10, the next level already takes us to N = 2^20, and the next level makes N greater than the largest existing computer memories. 22 QA 23