OpenCores
URL https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk

Subversion Repositories openrisc_me

[/] [openrisc/] [trunk/] [gnu-src/] [gcc-4.5.1/] [libstdc++-v3/] [doc/] [html/] [ext/] [pb_ds/] [pq_design.html] - Blame information for rev 424

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 424 jeremybenn
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
 
4
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
<head>
6
  <meta name="generator" content=
7
  "HTML Tidy for Linux/x86 (vers 12 April 2005), see www.w3.org" />
8
 
9
  <title>Priority-Queues</title>
10
  <meta http-equiv="Content-Type" content=
11
  "text/html; charset=us-ascii" />
12
  </head>
13
 
14
<body>
15
  <div id="page">
16
    <h1>Priority-Queue Design</h1>
17
 
18
    <h2><a name="overview" id="overview">Overview</a></h2>
19
 
20
    <p>The priority-queue container has the following
21
    declaration:</p>
22
    <pre>
23
<b>template</b>&lt;
24
    <b>typename</b> Value_Type,
25
    <b>typename</b> Cmp_Fn = std::less&lt;Value_Type&gt;,
26
    <b>typename</b> Tag = <a href="pairing_heap_tag.html">pairing_heap_tag</a>,
27
    <b>typename</b> Allocator = std::allocator&lt;<b>char</b>&gt; &gt;
28
<b>class</b> <a href="priority_queue.html">priority_queue</a>;
29
</pre>
30
 
31
    <p>The parameters have the following meaning:</p>
32
 
33
    <ol>
34
      <li><tt>Value_Type</tt> is the value type.</li>
35
 
36
      <li><tt>Cmp_Fn</tt> is a value comparison functor</li>
37
 
38
      <li><tt>Tag</tt> specifies which underlying data structure
39
      to use.</li>
40
 
41
      <li><tt>Allocator</tt> is an allocator
42
      type.</li>
43
    </ol>
44
 
45
    <p>The <tt>Tag</tt> parameter specifies which underlying
46
    data structure to use. Instantiating it by <a href=
47
    "pairing_heap_tag.html"><tt>pairing_heap_tag</tt></a>,
48
    <a href=
49
    "binary_heap_tag.html"><tt>binary_heap_tag</tt></a>,
50
    <a href=
51
    "binomial_heap_tag.html"><tt>binomial_heap_tag</tt></a>,
52
    <a href=
53
    "rc_binomial_heap_tag.html"><tt>rc_binomial_heap_tag</tt></a>,
54
    or <a href=
55
    "thin_heap_tag.html"><tt>thin_heap_tag</tt></a>,
56
    specifies, respectively, an underlying pairing heap [<a href=
57
    "references.html#fredman86pairing">fredman86pairing</a>],
58
    binary heap [<a href="references.html#clrs2001">clrs2001</a>],
59
    binomial heap [<a href=
60
    "references.html#clrs2001">clrs2001</a>], a binomial heap with
61
    a redundant binary counter [<a href=
62
    "references.html#maverik_lowerbounds">maverik_lowerbounds</a>],
63
    or a thin heap [<a href=
64
    "references.html#kt99fat_heaps">kt99fat_heas</a>]. These are
65
    explained further in <a href="#pq_imp">Implementations</a>.</p>
66
 
67
    <p>As mentioned in <a href=
68
    "tutorial.html#pq">Tutorial::Priority Queues</a>,
69
    <a href=
70
    "priority_queue.html"><tt>__gnu_pbds::priority_queue</tt></a>
71
    shares most of the same interface with <tt>std::priority_queue</tt>.
72
    <i>E.g.</i> if <tt>q</tt> is a priority queue of type
73
    <tt>Q</tt>, then <tt>q.top()</tt> will return the "largest"
74
    value in the container (according to <tt><b>typename</b>
75
    Q::cmp_fn</tt>). <a href=
76
    "priority_queue.html"><tt>__gnu_pbds::priority_queue</tt></a>
77
    has a larger (and very slightly different) interface than
78
    <tt>std::priority_queue</tt>, however, since typically
79
    <tt>push</tt> and <tt>pop</tt> are deemed insufficient for
80
    manipulating priority-queues. </p>
81
 
82
    <p>Different settings require different priority-queue
83
    implementations which are described in <a href=
84
    "#pq_imp">Implementations</a>; <a href="#pq_traits">Traits</a>
85
    discusses ways to differentiate between the different traits of
86
    different implementations.</p>
87
 
88
    <h2><a name="pq_it" id="pq_it">Iterators</a></h2>
89
 
90
    <p>There are many different underlying-data structures for
91
    implementing priority queues. Unfortunately, most such
92
    structures are oriented towards making <tt>push</tt> and
93
    <tt>top</tt> efficient, and consequently don't allow efficient
94
    access of other elements: for instance, they cannot support an efficient
95
    <tt>find</tt> method. In the use case where it
96
    is important to both access and "do something with" an
97
    arbitrary value, one would be out of luck. For example, many graph algorithms require
98
    modifying a value (typically increasing it in the sense of the
99
    priority queue's comparison functor).</p>
100
 
101
    <p>In order to access and manipulate an arbitrary value in a
102
    priority queue, one needs to reference the internals of the
103
    priority queue from some form of an associative container -
104
    this is unavoidable. Of course, in order to maintain the
105
    encapsulation of the priority queue, this needs to be done in a
106
    way that minimizes exposure to implementation internals.</p>
107
 
108
    <p>In <tt>pb_ds</tt> the priority queue's <tt>insert</tt>
109
    method returns an iterator, which if valid can be used for subsequent <tt>modify</tt> and
110
    <tt>erase</tt> operations. This both preserves the priority
111
    queue's encapsulation, and allows accessing arbitrary values (since the
112
    returned iterators from the <tt>push</tt> operation can be
113
    stored in some form of associative container).</p>
114
 
115
    <p>Priority queues' iterators present a problem regarding their
116
    invalidation guarantees. One assumes that calling
117
    <tt><b>operator</b>++</tt> on an iterator will associate it
118
    with the "next" value. Priority-queues are
119
    self-organizing: each operation changes what the "next" value
120
    means. Consequently, it does not make sense that <tt>push</tt>
121
    will return an iterator that can be incremented - this can have
122
    no possible use. Also, as in the case of hash-based containers,
123
    it is awkward to define if a subsequent <tt>push</tt> operation
124
    invalidates a prior returned iterator: it invalidates it in the
125
    sense that its "next" value is not related to what it
126
    previously considered to be its "next" value. However, it might not
127
    invalidate it, in the sense that it can be
128
    de-referenced and used for <tt>modify</tt> and <tt>erase</tt>
129
    operations.</p>
130
 
131
    <p>Similarly to the case of the other unordered associative
132
    containers, <tt>pb_ds</tt> uses a distinction between
133
    point-type and range type iterators. A priority queue's <tt>iterator</tt> can always be
134
    converted to a <tt>point_iterator</tt>, and a
135
    <tt>const_iterator</tt> can always be converted to a
136
    <tt>const_point_iterator</tt>.</p>
137
 
138
    <p>The following snippet demonstrates manipulating an arbitrary
139
    value:</p>
140
    <pre>
141
<i>// A priority queue of integers.</i>
142
<a href=
143
"priority_queue.html">priority_queue</a>&lt;<b>int</b>&gt; p;
144
 
145
<i>// Insert some values into the priority queue.</i>
146
<a href=
147
"priority_queue.html">priority_queue</a>&lt;<b>int</b>&gt;::point_iterator it = p.push(0);
148
 
149
p.push(1);
150
p.push(2);
151
 
152
<i>// Now modify a value.</i>
153
p.modify(it, 3);
154
 
155
assert(p.top() == 3);
156
</pre>
157
 
158
    <p>(<a href="pq_examples.html#xref">Priority Queue
159
    Examples::Cross-Referencing</a> shows a more detailed
160
    example.)</p>
161
 
162
    <p>It should be noted that an alternative design could embed an
163
    associative container in a priority queue. Could, but most probably should not. To begin with, it should be noted that one
164
    could always encapsulate a priority queue and an associative
165
    container mapping values to priority queue iterators with no
166
    performance loss. One cannot, however, "un-encapsulate" a
167
    priority queue embedding an associative container, which might
168
    lead to performance loss. Assume, that one needs to
169
    associate each value with some data unrelated to priority
170
    queues. Then using <tt>pb_ds</tt>'s design, one could use an
171
    associative container mapping each value to a pair consisting
172
    of this data and a priority queue's iterator. Using the
173
    embedded method would need to use two associative
174
    containers. Similar problems might arise in cases where a value
175
    can reside simultaneously in many priority queues.</p>
176
 
177
    <h2><a name="pq_imp" id="pq_imp">Implementations</a></h2>
178
 
179
    <p>There are three main implementations of priority queues: the
180
    first employs a binary heap, typically one which uses a
181
    sequence; the second uses a tree (or forest of trees), which is
182
    typically less structured than an associative container's tree;
183
    the third simply uses an associative container. These are
184
    shown, respectively, in Figures <a href=
185
    "#pq_different_underlying_dss">Underlying Priority-Queue
186
    Data-Structures</a> A1 and A2, Figure <a href=
187
    "#pq_different_underlying_dss">Underlying Priority-Queue
188
    Data-Structures</a> B, and Figures <a href=
189
    "#pq_different_underlying_dss">Underlying Priority-Queue
190
    Data-Structures</a> C.</p>
191
 
192
    <h6 class="c1"><a name="pq_different_underlying_dss" id=
193
    "pq_different_underlying_dss"><img src=
194
    "pq_different_underlying_dss.png" alt="no image" /></a></h6>
195
 
196
    <h6 class="c1">Underlying Priority-Queue Data-Structures.</h6>
197
 
198
    <p>Roughly speaking, any value that is both pushed and popped
199
    from a priority queue must incur a logarithmic expense (in the
200
    amortized sense). Any priority queue implementation that would
201
    avoid this, would violate known bounds on comparison-based
202
    sorting (see, <i>e.g.</i>, [<a href=
203
    "references.html#clrs2001">clrs2001</a>] and <a href=
204
    "references.html#brodal96priority">brodal96priority</a>]).</p>
205
 
206
    <p>Most implementations do
207
    not differ in the asymptotic amortized complexity of
208
    <tt>push</tt> and <tt>pop</tt> operations, but they differ in
209
    the constants involved, in the complexity of other operations
210
    (<i>e.g.</i>, <tt>modify</tt>), and in the worst-case
211
    complexity of single operations. In general, the more
212
    "structured" an implementation (<i>i.e.</i>, the more internal
213
    invariants it possesses) - the higher its amortized complexity
214
    of <tt>push</tt> and <tt>pop</tt> operations.</p>
215
 
216
    <p><tt>pb_ds</tt> implements different algorithms using a
217
    single class: <a href="priority_queue.html">priority_queue</a>.
218
    Instantiating the <tt>Tag</tt> template parameter, "selects"
219
    the implementation:</p>
220
 
221
    <ol>
222
      <li>Instantiating <tt>Tag = <a href=
223
      "binary_heap_tag.html">binary_heap_tag</a></tt> creates
224
      a binary heap of the form in Figures <a href=
225
      "#pq_different_underlying_dss">Underlying Priority-Queue
226
      Data-Structures</a> A1 or A2. The former is internally
227
      selected by <a href="priority_queue.html">priority_queue</a>
228
      if <tt>Value_Type</tt> is instantiated by a primitive type
229
      (<i>e.g.</i>, an <tt><b>int</b></tt>); the latter is
230
      internally selected for all other types (<i>e.g.</i>,
231
      <tt>std::string</tt>). This implementations is relatively
232
      unstructured, and so has good <tt>push</tt> and <tt>pop</tt>
233
      performance; it is the "best-in-kind" for primitive
234
      types, <i>e.g.</i>, <tt><b>int</b></tt>s. Conversely, it has
235
      high worst-case performance, and can support only linear-time
236
      <tt>modify</tt> and <tt>erase</tt> operations; this is
237
      explained further in <a href="#pq_traits">Traits</a>.</li>
238
 
239
      <li>Instantiating <tt>Tag = <a href=
240
      "pairing_heap_tag.html">pairing_heap_tag</a></tt>
241
      creates a pairing heap of the form in Figure <a href=
242
      "#pq_different_underlying_dss">Underlying Priority-Queue
243
      Data-Structures</a> B. This implementations too is relatively
244
      unstructured, and so has good <tt>push</tt> and <tt>pop</tt>
245
      performance; it is the "best-in-kind" for non-primitive
246
      types, <i>e.g.</i>, <tt>std:string</tt>s. It also has very
247
      good worst-case <tt>push</tt> and <tt>join</tt> performance
248
      (<i>O(1)</i>), but has high worst-case <tt>pop</tt>
249
      complexity.</li>
250
 
251
      <li>Instantiating <tt>Tag = <a href=
252
      "binomial_heap_tag.html">binomial_heap_tag</a></tt>
253
      creates a binomial heap of the form in Figure <a href=
254
      "#pq_different_underlying_dss">Underlying Priority-Queue
255
      Data-Structures</a> B. This implementations is more
256
      structured than a pairing heap, and so has worse
257
      <tt>push</tt> and <tt>pop</tt> performance. Conversely, it
258
      has sub-linear worst-case bounds for <tt>pop</tt>,
259
      <i>e.g.</i>, and so it might be preferred in cases where
260
      responsiveness is important.</li>
261
 
262
      <li>Instantiating <tt>Tag = <a href=
263
      "rc_binomial_heap_tag.html">rc_binomial_heap_tag</a></tt>
264
      creates a binomial heap of the form in Figure <a href=
265
      "#pq_different_underlying_dss">Underlying Priority-Queue
266
      Data-Structures</a> B, accompanied by a redundant counter
267
      which governs the trees. This implementations is therefore
268
      more structured than a binomial heap, and so has worse
269
      <tt>push</tt> and <tt>pop</tt> performance. Conversely, it
270
      guarantees <i>O(1)</i> <tt>push</tt> complexity, and so it
271
      might be preferred in cases where the responsiveness of a
272
      binomial heap is insufficient.</li>
273
 
274
      <li>Instantiating <tt>Tag = <a href=
275
      "thin_heap_tag.html">thin_heap_tag</a></tt> creates a
276
      thin heap of the form in Figure <a href=
277
      "#pq_different_underlying_dss">Underlying Priority-Queue
278
      Data-Structures</a> B. This implementations too is more
279
      structured than a pairing heap, and so has worse
280
      <tt>push</tt> and <tt>pop</tt> performance. Conversely, it
281
      has better worst-case and identical amortized complexities
282
      than a Fibonacci heap, and so might be more appropriate for
283
      some graph algorithms.</li>
284
    </ol>
285
 
286
    <p><a href="pq_performance_tests.html">Priority-Queue
287
    Performance Tests</a> shows some results for the above, and
288
    discusses these points further.</p>
289
 
290
    <p>Of course, one can use any order-preserving associative
291
    container as a priority queue, as in Figure <a href=
292
    "#pq_different_underlying_dss">Underlying Priority-Queue
293
    Data-Structures</a> C, possibly by creating an adapter class
294
    over the associative container (much as
295
    <tt>std::priority_queue</tt> can adapt <tt>std::vector</tt>).
296
    This has the advantage that no cross-referencing is necessary
297
    at all; the priority queue itself is an associative container.
298
    Most associative containers are too structured to compete with
299
    priority queues in terms of <tt>push</tt> and <tt>pop</tt>
300
    performance.</p>
301
 
302
    <h2><a name="pq_traits" id="pq_traits">Traits</a></h2>
303
 
304
    <p>It would be nice if all priority queues could
305
    share exactly the same behavior regardless of implementation. Sadly, this is not possible. Just one for instance is in join operations: joining
306
    two binary heaps might throw an exception (not corrupt
307
    any of the heaps on which it operates), but joining two pairing
308
    heaps is exception free.</p>
309
 
310
    <p>Tags and traits are very useful for manipulating generic
311
    types. <a href=
312
    "priority_queue.html"><tt>__gnu_pbds::priority_queue</tt></a>
313
    publicly defines <tt>container_category</tt> as one of the tags
314
    discussed in <a href="#pq_imp">Implementations</a>. Given any
315
    container <tt>Cntnr</tt>, the tag of the underlying
316
    data structure can be found via <tt><b>typename</b>
317
    Cntnr::container_category</tt>; this is one of the types shown in
318
    Figure <a href="#pq_tag_cd">Data-structure tag class
319
    hierarchy</a>.</p>
320
 
321
    <h6 class="c1"><a name="pq_tag_cd" id=
322
    "pq_tag_cd"><img src="priority_queue_tag_cd.png" alt=
323
    "no image" /></a></h6>
324
 
325
    <h6 class="c1">Data-structure tag class hierarchy.</h6>
326
 
327
    <p>Additionally, a traits mechanism can be used to query a
328
    container type for its attributes. Given any container
329
    <tt>Cntnr</tt>, then <tt><a href=
330
    "assoc_container_traits.html">__gnu_pbds::container_traits</a>&lt;Cntnr&gt;</tt>
331
    is a traits class identifying the properties of the
332
    container.</p>
333
 
334
    <p>To find if a container might throw if two of its objects are
335
    joined, one can use <a href=
336
    "assoc_container_traits.html"><tt>container_traits</tt></a><tt>&lt;Cntnr&gt;::split_join_can_throw</tt>,
337
    for example.</p>
338
 
339
    <p>Different priority-queue implementations have different invalidation guarantees. This is
340
    especially important, since as explained in <a href=
341
    "#pq_it">Iterators</a>, there is no way to access an arbitrary
342
    value of priority queues except for iterators. Similarly to
343
    associative containers, one can use
344
    <a href=
345
    "assoc_container_traits.html"><tt>container_traits</tt></a><tt>&lt;Cntnr&gt;::invalidation_guarantee</tt>
346
    to get the invalidation guarantee type of a priority queue.</p>
347
 
348
    <p>It is easy to understand from Figure <a href=
349
    "#pq_different_underlying_dss">Underlying Priority-Queue
350
    Data-Structures</a>, what <a href=
351
    "assoc_container_traits.html"><tt>container_traits</tt></a><tt>&lt;Cntnr&gt;::invalidation_guarantee</tt>
352
    will be for different implementations. All implementations of
353
    type <a href="#pq_different_underlying_dss">Underlying
354
    Priority-Queue Data-Structures</a> B have <a href=
355
    "point_invalidation_guarantee.html"><tt>point_invalidation_guarantee</tt></a>:
356
    the container can freely internally reorganize the nodes -
357
    range-type iterators are invalidated, but point-type iterators
358
    are always valid. Implementations of type <a href=
359
    "#pq_different_underlying_dss">Underlying Priority-Queue
360
    Data-Structures</a> A1 and A2 have <a href=
361
    "basic_invalidation_guarantee.html"><tt>basic_invalidation_guarantee</tt></a>:
362
    the container can freely internally reallocate the array - both
363
    point-type and range-type iterators might be invalidated.</p>
364
 
365
    <p>This has major implications, and constitutes a good reason to avoid
366
    using binary heaps. A binary heap can perform <tt>modify</tt>
367
    or <tt>erase</tt> efficiently <u>given a valid point-type
368
    iterator</u>. However, inn order to supply it with a valid point-type
369
    iterator, one needs to iterate (linearly) over all
370
    values, then supply the relevant iterator (recall that a
371
    range-type iterator can always be converted to a point-type
372
    iterator). This means that if the number of <tt>modify</tt> or
373
    <tt>erase</tt> operations is non-negligible (say
374
    super-logarithmic in the total sequence of operations) - binary
375
    heaps will perform badly.</p>
376
    <pre>
377
 
378
</pre>
379
  </div>
380
</body>
381
</html>

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.