1 //
2 //=======================================================================
3 // Copyright 2009 Trustees of Indiana University
4 // Authors: Jeremiah J. Willcock, Andrew Lumsdaine
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See
7 // accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 //=======================================================================
10 //
11 #ifndef BOOST_D_ARY_HEAP_HPP
12 #define BOOST_D_ARY_HEAP_HPP
13
14 #include <vector>
15 #include <cstddef>
16 #include <algorithm>
17 #include <utility>
18 #include <boost/assert.hpp>
19 #include <boost/static_assert.hpp>
20 #include <boost/shared_array.hpp>
21 #include <boost/property_map/property_map.hpp>
22
23 // WARNING: it is not safe to copy a d_ary_heap_indirect and then modify one of
24 // the copies. The class is required to be copyable so it can be passed around
25 // (without move support from C++11), but it deep-copies the heap contents yet
26 // shallow-copies the index_in_heap_map.
27
28 namespace boost
29 {
30
31 // Swap two elements in a property map without assuming they model
32 // LvaluePropertyMap -- currently not used
33 template < typename PropMap >
property_map_swap(PropMap prop_map,const typename boost::property_traits<PropMap>::key_type & ka,const typename boost::property_traits<PropMap>::key_type & kb)34 inline void property_map_swap(PropMap prop_map,
35 const typename boost::property_traits< PropMap >::key_type& ka,
36 const typename boost::property_traits< PropMap >::key_type& kb)
37 {
38 typename boost::property_traits< PropMap >::value_type va
39 = get(prop_map, ka);
40 put(prop_map, ka, get(prop_map, kb));
41 put(prop_map, kb, va);
42 }
43
44 namespace detail
45 {
46 template < typename Value > class fixed_max_size_vector
47 {
48 boost::shared_array< Value > m_data;
49 std::size_t m_size;
50
51 public:
52 typedef std::size_t size_type;
fixed_max_size_vector(std::size_t max_size)53 fixed_max_size_vector(std::size_t max_size)
54 : m_data(new Value[max_size]), m_size(0)
55 {
56 }
size() const57 std::size_t size() const { return m_size; }
empty() const58 bool empty() const { return m_size == 0; }
operator [](std::size_t i)59 Value& operator[](std::size_t i) { return m_data[i]; }
operator [](std::size_t i) const60 const Value& operator[](std::size_t i) const { return m_data[i]; }
push_back(Value v)61 void push_back(Value v) { m_data[m_size++] = v; }
pop_back()62 void pop_back() { --m_size; }
back()63 Value& back() { return m_data[m_size - 1]; }
back() const64 const Value& back() const { return m_data[m_size - 1]; }
65 };
66 }
67
68 // D-ary heap using an indirect compare operator (use identity_property_map
69 // as DistanceMap to get a direct compare operator). This heap appears to be
70 // commonly used for Dijkstra's algorithm for its good practical performance
71 // on some platforms; asymptotically, it has an O(lg N) decrease-key
72 // operation while that can be done in constant time on a relaxed heap. The
73 // implementation is mostly based on the binary heap page on Wikipedia and
74 // online sources that state that the operations are the same for d-ary
75 // heaps. This code is not based on the old Boost d-ary heap code.
76 //
77 // - d_ary_heap_indirect is a model of UpdatableQueue as is needed for
78 // dijkstra_shortest_paths.
79 //
80 // - Value must model Assignable.
81 // - Arity must be at least 2 (optimal value appears to be 4, both in my and
82 // third-party experiments).
83 // - IndexInHeapMap must be a ReadWritePropertyMap from Value to
84 // Container::size_type (to store the index of each stored value within the
85 // heap for decrease-key aka update).
86 // - DistanceMap must be a ReadablePropertyMap from Value to something
87 // (typedef'ed as distance_type).
88 // - Compare must be a BinaryPredicate used as a less-than operator on
89 // distance_type.
90 // - Container must be a random-access, contiguous container (in practice,
91 // the operations used probably require that it is std::vector<Value>).
92 //
93 template < typename Value, std::size_t Arity, typename IndexInHeapPropertyMap,
94 typename DistanceMap, typename Compare = std::less< Value >,
95 typename Container = std::vector< Value > >
96 class d_ary_heap_indirect
97 {
98 BOOST_STATIC_ASSERT(Arity >= 2);
99
100 public:
101 typedef typename Container::size_type size_type;
102 typedef Value value_type;
103 typedef typename boost::property_traits< DistanceMap >::value_type key_type;
104 typedef DistanceMap key_map;
105
d_ary_heap_indirect(DistanceMap distance,IndexInHeapPropertyMap index_in_heap,const Compare & compare=Compare (),const Container & data=Container ())106 d_ary_heap_indirect(DistanceMap distance,
107 IndexInHeapPropertyMap index_in_heap,
108 const Compare& compare = Compare(), const Container& data = Container())
109 : compare(compare)
110 , data(data)
111 , distance(distance)
112 , index_in_heap(index_in_heap)
113 {
114 }
115 /* Implicit copy constructor */
116 /* Implicit assignment operator */
117
size() const118 size_type size() const { return data.size(); }
119
empty() const120 bool empty() const { return data.empty(); }
121
push(const Value & v)122 void push(const Value& v)
123 {
124 size_type index = data.size();
125 data.push_back(v);
126 put(index_in_heap, v, index);
127 preserve_heap_property_up(index);
128 verify_heap();
129 }
130
top()131 Value& top()
132 {
133 BOOST_ASSERT(!this->empty());
134 return data[0];
135 }
136
top() const137 const Value& top() const
138 {
139 BOOST_ASSERT(!this->empty());
140 return data[0];
141 }
142
pop()143 void pop()
144 {
145 BOOST_ASSERT(!this->empty());
146 put(index_in_heap, data[0], (size_type)(-1));
147 if (data.size() != 1)
148 {
149 data[0] = data.back();
150 put(index_in_heap, data[0], (size_type)(0));
151 data.pop_back();
152 preserve_heap_property_down();
153 verify_heap();
154 }
155 else
156 {
157 data.pop_back();
158 }
159 }
160
161 // This function assumes the key has been updated (using an external write
162 // to the distance map or such)
163 // See
164 // http://coding.derkeiler.com/Archive/General/comp.theory/2007-05/msg00043.html
update(const Value & v)165 void update(const Value& v)
166 { /* decrease-key */
167 size_type index = get(index_in_heap, v);
168 preserve_heap_property_up(index);
169 verify_heap();
170 }
171
contains(const Value & v) const172 bool contains(const Value& v) const
173 {
174 size_type index = get(index_in_heap, v);
175 return (index != (size_type)(-1));
176 }
177
push_or_update(const Value & v)178 void push_or_update(const Value& v)
179 { /* insert if not present, else update */
180 size_type index = get(index_in_heap, v);
181 if (index == (size_type)(-1))
182 {
183 index = data.size();
184 data.push_back(v);
185 put(index_in_heap, v, index);
186 }
187 preserve_heap_property_up(index);
188 verify_heap();
189 }
190
keys() const191 DistanceMap keys() const { return distance; }
192
193 private:
194 Compare compare;
195 Container data;
196 DistanceMap distance;
197 IndexInHeapPropertyMap index_in_heap;
198
199 // The distances being compared using compare and that are stored in the
200 // distance map
201 typedef typename boost::property_traits< DistanceMap >::value_type
202 distance_type;
203
204 // Get the parent of a given node in the heap
parent(size_type index)205 static size_type parent(size_type index) { return (index - 1) / Arity; }
206
207 // Get the child_idx'th child of a given node; 0 <= child_idx < Arity
child(size_type index,std::size_t child_idx)208 static size_type child(size_type index, std::size_t child_idx)
209 {
210 return index * Arity + child_idx + 1;
211 }
212
213 // Swap two elements in the heap by index, updating index_in_heap
swap_heap_elements(size_type index_a,size_type index_b)214 void swap_heap_elements(size_type index_a, size_type index_b)
215 {
216 using std::swap;
217 Value value_a = data[index_a];
218 Value value_b = data[index_b];
219 data[index_a] = value_b;
220 data[index_b] = value_a;
221 put(index_in_heap, value_a, index_b);
222 put(index_in_heap, value_b, index_a);
223 }
224
225 // Emulate the indirect_cmp that is now folded into this heap class
compare_indirect(const Value & a,const Value & b) const226 bool compare_indirect(const Value& a, const Value& b) const
227 {
228 return compare(get(distance, a), get(distance, b));
229 }
230
231 // Verify that the array forms a heap; commented out by default
verify_heap() const232 void verify_heap() const
233 {
234 // This is a very expensive test so it should be disabled even when
235 // NDEBUG is not defined
236 #if 0
237 for (size_t i = 1; i < data.size(); ++i) {
238 if (compare_indirect(data[i], data[parent(i)])) {
239 BOOST_ASSERT (!"Element is smaller than its parent");
240 }
241 }
242 #endif
243 }
244
245 // Starting at a node, move up the tree swapping elements to preserve the
246 // heap property
preserve_heap_property_up(size_type index)247 void preserve_heap_property_up(size_type index)
248 {
249 size_type orig_index = index;
250 size_type num_levels_moved = 0;
251 // The first loop just saves swaps that need to be done in order to
252 // avoid aliasing issues in its search; there is a second loop that does
253 // the necessary swap operations
254 if (index == 0)
255 return; // Do nothing on root
256 Value currently_being_moved = data[index];
257 distance_type currently_being_moved_dist
258 = get(distance, currently_being_moved);
259 for (;;)
260 {
261 if (index == 0)
262 break; // Stop at root
263 size_type parent_index = parent(index);
264 Value parent_value = data[parent_index];
265 if (compare(
266 currently_being_moved_dist, get(distance, parent_value)))
267 {
268 ++num_levels_moved;
269 index = parent_index;
270 continue;
271 }
272 else
273 {
274 break; // Heap property satisfied
275 }
276 }
277 // Actually do the moves -- move num_levels_moved elements down in the
278 // tree, then put currently_being_moved at the top
279 index = orig_index;
280 for (size_type i = 0; i < num_levels_moved; ++i)
281 {
282 size_type parent_index = parent(index);
283 Value parent_value = data[parent_index];
284 put(index_in_heap, parent_value, index);
285 data[index] = parent_value;
286 index = parent_index;
287 }
288 data[index] = currently_being_moved;
289 put(index_in_heap, currently_being_moved, index);
290 verify_heap();
291 }
292
293 // From the root, swap elements (each one with its smallest child) if there
294 // are any parent-child pairs that violate the heap property
preserve_heap_property_down()295 void preserve_heap_property_down()
296 {
297 if (data.empty())
298 return;
299 size_type index = 0;
300 Value currently_being_moved = data[0];
301 distance_type currently_being_moved_dist
302 = get(distance, currently_being_moved);
303 size_type heap_size = data.size();
304 Value* data_ptr = &data[0];
305 for (;;)
306 {
307 size_type first_child_index = child(index, 0);
308 if (first_child_index >= heap_size)
309 break; /* No children */
310 Value* child_base_ptr = data_ptr + first_child_index;
311 size_type smallest_child_index = 0;
312 distance_type smallest_child_dist
313 = get(distance, child_base_ptr[smallest_child_index]);
314 if (first_child_index + Arity <= heap_size)
315 {
316 // Special case for a statically known loop count (common case)
317 for (size_t i = 1; i < Arity; ++i)
318 {
319 Value i_value = child_base_ptr[i];
320 distance_type i_dist = get(distance, i_value);
321 if (compare(i_dist, smallest_child_dist))
322 {
323 smallest_child_index = i;
324 smallest_child_dist = i_dist;
325 }
326 }
327 }
328 else
329 {
330 for (size_t i = 1; i < heap_size - first_child_index; ++i)
331 {
332 distance_type i_dist = get(distance, child_base_ptr[i]);
333 if (compare(i_dist, smallest_child_dist))
334 {
335 smallest_child_index = i;
336 smallest_child_dist = i_dist;
337 }
338 }
339 }
340 if (compare(smallest_child_dist, currently_being_moved_dist))
341 {
342 swap_heap_elements(
343 smallest_child_index + first_child_index, index);
344 index = smallest_child_index + first_child_index;
345 continue;
346 }
347 else
348 {
349 break; // Heap property satisfied
350 }
351 }
352 verify_heap();
353 }
354 };
355
356 } // namespace boost
357
358 #endif // BOOST_D_ARY_HEAP_HPP
359