Skip to content

Commit 4c892d8

Browse files
committed
work
1 parent e0e34e4 commit 4c892d8

File tree

2 files changed

+219
-45
lines changed

2 files changed

+219
-45
lines changed

ortools/algorithms/adjustable_k_ary_heap.h

Lines changed: 137 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@
2020
#include <vector>
2121

2222
#include "absl/log/check.h"
23+
#include "absl/types/span.h"
24+
#include "ortools/base/strong_int.h"
25+
26+
namespace internal {
27+
template <typename T, bool IsStrong>
28+
struct ArrayIndexTypeHelper {
29+
using type = T;
30+
};
31+
32+
template <typename T>
33+
struct ArrayIndexTypeHelper<T, true> {
34+
using type = typename T::ValueType;
35+
};
36+
37+
template <typename T>
38+
using ArrayIndexType =
39+
typename ArrayIndexTypeHelper<T, util_intops::IsStrongInt<T>::value>::type;
40+
} // namespace internal
2341

2442
// Adjustable k-ary heap for std::pair<Priority, Index> classes containing a
2543
// priority and an index referring to an array where the relevant data is
@@ -53,24 +71,25 @@ class AdjustableKAryHeap {
5371

5472
// Construct a k-heap from an existing vector, tracking original indices.
5573
// `universe_size` is the maximum possible index in `elements`.
56-
explicit AdjustableKAryHeap(const std::vector<Aggregate>& elements,
74+
explicit AdjustableKAryHeap(const absl::Span<const Aggregate> elements,
5775
HeapIndex universe_size) {
5876
Load(elements, universe_size);
5977
}
6078

61-
explicit AdjustableKAryHeap(const std::vector<Index>& indices,
62-
const std::vector<Priority>& priorities,
79+
explicit AdjustableKAryHeap(const absl::Span<const Index> indices,
80+
const absl::Span<const Priority> priorities,
6381
HeapIndex universe_size) {
6482
Load(indices, priorities, universe_size);
6583
}
6684

6785
void Clear() {
6886
data_.clear();
6987
heap_positions_.clear();
70-
heap_size_ = 0;
88+
heap_size_ = HeapIndex(0);
7189
}
7290

73-
void Load(const std::vector<Aggregate>& elements, HeapIndex universe_size) {
91+
void Load(const absl::Span<const Aggregate> elements,
92+
HeapIndex universe_size) {
7493
data_.resize(elements.size());
7594
heap_size_ = elements.size();
7695
std::copy(elements.begin(), elements.end(), data_.begin());
@@ -81,14 +100,21 @@ class AdjustableKAryHeap {
81100
BuildHeap();
82101
}
83102

84-
void Load(const std::vector<Index>& indices,
85-
const std::vector<Priority>& priorities, HeapIndex universe_size) {
103+
void Load(const absl::Span<const Index> indices,
104+
const absl::Span<const Priority> priorities,
105+
HeapIndex universe_size) {
106+
CHECK_EQ(indices.size(), priorities.size());
107+
data_.resize(indices.size());
108+
indices_.resize(indices.size());
109+
priorities_.resize(indices.size());
86110
std::copy(indices.begin(), indices.end(), indices_.begin());
87111
std::copy(priorities.begin(), priorities.end(), priorities_.begin());
88-
heap_size_ = indices.size();
89-
heap_positions_.resize(universe_size, kNonExistent);
90-
for (HeapIndex i = 0; i < data_.size(); ++i) {
91-
heap_positions_[indices_[i]] = i;
112+
heap_size_ = HeapIndex(indices.size());
113+
heap_positions_.resize(GetHeapIndexAsArrayIndex(universe_size),
114+
kNonExistent);
115+
for (HeapIndex i{0}; i < HeapIndex(data_.size()); ++i) {
116+
heap_positions_[GetIndexAsArrayIndex(
117+
indices_[GetHeapIndexAsArrayIndex(i)])] = i;
92118
}
93119
BuildHeap();
94120
}
@@ -98,7 +124,7 @@ class AdjustableKAryHeap {
98124
// This will CHECK-fail if the heap is empty (through Top()).
99125
void Pop() {
100126
CHECK(!IsEmpty());
101-
CHECK(RemoveAtHeapPosition(0));
127+
CHECK(RemoveAtHeapPosition(HeapIndex(0)));
102128
}
103129

104130
// Returns the index of the top element, without modifying the heap.
@@ -109,31 +135,47 @@ class AdjustableKAryHeap {
109135
return data_[0].second;
110136
}
111137

112-
// Returns the index of the top element, without modifying the heap.
138+
// Returns the index of the bottom element, without modifying the heap.
139+
// This operation does not remove the element from the heap.
140+
Index BottomIndex() const {
141+
CHECK(!IsEmpty());
142+
return data_[GetHeapIndexAsArrayIndex(GetLowestPriorityChild(HeapIndex(0)))]
143+
.second;
144+
}
145+
146+
// Returns the priority of the top element, without modifying the heap.
113147
// Note that this does not remove the element from the heap, Pop() must be
114148
// called explicitly.
115-
116149
Priority TopPriority() const {
117150
CHECK(!IsEmpty());
118151
return data_[0].first;
119152
}
120153

154+
// Returns the priority of the bottom element, without modifying the heap.
155+
// Note that this does not remove the element from the heap.
156+
Priority BottomPriority() const {
157+
CHECK(!IsEmpty());
158+
return data_[GetHeapIndexAsArrayIndex(GetLowestPriorityChild(HeapIndex(0)))]
159+
.first;
160+
}
161+
121162
// Returns the number of elements in the heap.
122163
HeapIndex heap_size() const { return heap_size_; }
123164

124165
// True iff the heap is empty.
125-
bool IsEmpty() const { return heap_size() == 0; }
166+
bool IsEmpty() const { return heap_size() == HeapIndex(0); }
126167

127168
// Insert an element into the heap.
128169
void Insert(Aggregate element) {
129170
const Index index = element.second;
130-
if (index >= heap_positions_.size()) {
131-
heap_positions_.resize(index + 1, kNonExistent);
171+
if (index >= Index(heap_positions_.size())) {
172+
heap_positions_.resize(GetIndexAsArrayIndex(index) + 1, kNonExistent);
132173
}
133174
if (GetHeapPosition(index) == kNonExistent) {
134-
heap_positions_[index] = heap_size_;
135-
if (heap_size_ < data_.size()) {
136-
data_[heap_size_] = element;
175+
heap_positions_[GetIndexAsArrayIndex(index)] = heap_size_;
176+
177+
if (heap_size_ < HeapIndex(data_.size())) {
178+
data_[GetHeapIndexAsArrayIndex(heap_size_)] = element;
137179
} else {
138180
data_.push_back(element);
139181
}
@@ -155,9 +197,9 @@ class AdjustableKAryHeap {
155197
void Update(Aggregate element) {
156198
DCHECK(!IsEmpty());
157199
const HeapIndex heap_position = GetHeapPosition(element.second);
158-
DCHECK_GE(heap_position, 0);
159-
DCHECK_LT(heap_position, heap_positions_.size());
160-
data_[heap_position] = element;
200+
DCHECK_GE(heap_position, HeapIndex(0));
201+
DCHECK_LT(heap_position, HeapIndex(heap_positions_.size()));
202+
data_[GetHeapIndexAsArrayIndex(heap_position)] = element;
161203
if (HasPriority(heap_position, Parent(heap_position))) {
162204
SiftUp(heap_position);
163205
} else {
@@ -172,53 +214,55 @@ class AdjustableKAryHeap {
172214

173215
// Checks that the heap is well-formed.
174216
bool CheckHeapProperty() const {
175-
for (HeapIndex i = heap_size() - 1; i >= Arity; --i) {
217+
for (HeapIndex i = heap_size() - HeapIndex(1);
218+
GetHeapIndexAsArrayIndex(i) >= Arity; --i) {
176219
CHECK(HasPriority(Parent(i), i))
177220
<< "Parent " << Parent(i) << " with priority " << priority(Parent(i))
178221
<< " does not have priority over " << i << " with priority "
179222
<< priority(i) << " , heap_size = " << heap_size()
180223
<< ", priority difference = " << priority(i) - priority(Parent(i));
181224
}
182-
CHECK_LE(heap_size(), heap_positions_.size());
183-
CHECK_LE(heap_size(), data_.size());
225+
CHECK_LE(heap_size(), HeapIndex(heap_positions_.size()));
226+
CHECK_LE(heap_size(), HeapIndex(data_.size()));
184227
return true;
185228
}
186229

187230
private:
188231
// Gets the current position of element with index i in the heap.
189232
HeapIndex GetHeapPosition(Index i) const {
190-
DCHECK_GE(i, 0);
191-
DCHECK_LT(i, heap_positions_.size());
192-
return heap_positions_[i];
233+
DCHECK_GE(i, Index(0));
234+
DCHECK_LT(i, Index(heap_positions_.size()));
235+
return heap_positions_[GetIndexAsArrayIndex(i)];
193236
}
194237

195238
// Removes an element at a given heap position.
196239
bool RemoveAtHeapPosition(HeapIndex heap_index) {
197240
DCHECK(!IsEmpty());
198-
DCHECK_GE(heap_index, 0);
241+
DCHECK_GE(GetHeapIndexAsArrayIndex(heap_index), 0);
242+
199243
if (heap_index >= heap_size()) return false;
200-
PerformSwap(heap_index, heap_size() - 1);
244+
PerformSwap(heap_index, heap_size() - HeapIndex(1));
201245
--heap_size_;
202246
if (HasPriority(heap_index, Parent(heap_index))) {
203247
SiftUp(heap_index);
204248
} else {
205249
SiftDown(heap_index);
206250
}
207-
heap_positions_[index(heap_size_)] = kNonExistent;
251+
heap_positions_[GetIndexAsArrayIndex(index(heap_size_))] = kNonExistent;
208252
return true;
209253
}
210254

211255
// Maintains heap property by sifting down starting from the end,
212256
void BuildHeap() {
213-
for (HeapIndex i = Parent(heap_size()); i >= 0; --i) {
257+
for (HeapIndex i = Parent(heap_size()); i >= HeapIndex(0); --i) {
214258
SiftDown(i);
215259
}
216260
DCHECK(CheckHeapProperty());
217261
}
218262

219263
// Maintains heap property by sifting up an element.
220264
void SiftUp(HeapIndex index) {
221-
while (index > 0 && HasPriority(index, Parent(index))) {
265+
while (index > HeapIndex(0) && HasPriority(index, Parent(index))) {
222266
PerformSwap(index, Parent(index));
223267
index = Parent(index);
224268
}
@@ -238,7 +282,8 @@ class AdjustableKAryHeap {
238282
// smallest (resp. largest) key for a min- (resp. max-) heap.
239283
// Returns index if there are no such children.
240284
HeapIndex GetHighestPriorityChild(HeapIndex index) const {
241-
const HeapIndex right_bound = std::min(RightChild(index) + 1, heap_size());
285+
const HeapIndex right_bound =
286+
std::min(RightChild(index) + HeapIndex(1), heap_size());
242287
HeapIndex highest_priority_child = index;
243288
for (HeapIndex i = LeftChild(index); i < right_bound; ++i) {
244289
if (HasPriority(i, highest_priority_child)) {
@@ -248,18 +293,37 @@ class AdjustableKAryHeap {
248293
return highest_priority_child;
249294
}
250295

296+
// Finds the child with the lowest priority, i.e. the child with the
297+
// largest (resp. smallest) key for a min- (resp. max-) heap.
298+
// Returns index if there are no such children.
299+
HeapIndex GetLowestPriorityChild(HeapIndex index) const {
300+
const HeapIndex right_bound =
301+
std::min(RightChild(index) + HeapIndex(1), heap_size());
302+
HeapIndex highest_priority_child = index;
303+
for (HeapIndex i = LeftChild(index); i < right_bound; ++i) {
304+
if (!HasPriority(i, highest_priority_child)) {
305+
highest_priority_child = i;
306+
}
307+
}
308+
return highest_priority_child;
309+
}
310+
251311
// Swaps two elements of data_, while also making sure heap_positions_ is
252312
// properly maintained.
253313
void PerformSwap(HeapIndex i, HeapIndex j) {
254-
std::swap(data_[i], data_[j]);
255-
std::swap(heap_positions_[index(i)], heap_positions_[index(j)]);
314+
std::swap(data_[GetIndexAsArrayIndex(i)], data_[GetIndexAsArrayIndex(j)]);
315+
std::swap(heap_positions_[GetIndexAsArrayIndex(index(i))],
316+
heap_positions_[GetIndexAsArrayIndex(index(j))]);
256317
}
257318

258319
// Compares two elements based on whether we are dealing with a min- or a
259320
// max-heap. Returns true if (data indexed by) i has more priority
260321
// than j. Note that we only use operator::<.
261322
bool HasPriority(HeapIndex i, HeapIndex j) const {
262-
return IsMaxHeap ? data_[j] < data_[i] : data_[i] < data_[j];
323+
return IsMaxHeap
324+
? data_[GetIndexAsArrayIndex(j)] < data_[GetIndexAsArrayIndex(i)]
325+
: data_[GetIndexAsArrayIndex(i)] <
326+
data_[GetIndexAsArrayIndex(j)];
263327
}
264328

265329
// Since Arity is a (small) constant, we expect compilers to avoid
@@ -268,23 +332,51 @@ class AdjustableKAryHeap {
268332
// Powers of 2 are guaranteed to be quick thanks to simple shifts.
269333

270334
// Gets the leftmost child index of a given node
271-
HeapIndex LeftChild(HeapIndex index) const { return Arity * index + 1; }
335+
HeapIndex LeftChild(HeapIndex index) const {
336+
return Arity * index + HeapIndex(1);
337+
}
272338

273339
// Gets the rightmost child index of a given node
274-
HeapIndex RightChild(HeapIndex index) const { return Arity * (index + 1); }
340+
HeapIndex RightChild(HeapIndex index) const {
341+
return Arity * (index + HeapIndex(1));
342+
}
275343

276344
// For division, the optimization is more uncertain, although a simple
277345
// multiplication and a shift might be used by the compiler.
278346
// Of course, powers of 2 are guaranteed to be quick thanks to simple shifts.
279347

280348
// Gets the parent index of a given index.
281-
HeapIndex Parent(HeapIndex index) const { return (index - 1) / Arity; }
349+
HeapIndex Parent(HeapIndex index) const {
350+
return (index - HeapIndex(1)) / Arity;
351+
}
282352

283353
// Returns the index of the element at position i in the heap.
284-
Index index(HeapIndex i) const { return data_[i].second; }
354+
Index index(HeapIndex i) const {
355+
return data_[GetIndexAsArrayIndex(i)].second;
356+
}
285357

286358
// Returns the index of the element at position i in the heap.
287-
Priority priority(HeapIndex i) const { return data_[i].first; }
359+
Priority priority(HeapIndex i) const {
360+
return data_[GetHeapIndexAsArrayIndex(i)].first;
361+
}
362+
363+
// Returns the index as an array index.
364+
internal::ArrayIndexType<Index> GetIndexAsArrayIndex(Index i) const {
365+
if constexpr (util_intops::IsStrongInt<Index>::value) {
366+
return i.value();
367+
} else {
368+
return i;
369+
}
370+
}
371+
// Returns the heap index as an array index.
372+
internal::ArrayIndexType<HeapIndex> GetHeapIndexAsArrayIndex(
373+
HeapIndex i) const {
374+
if constexpr (util_intops::IsStrongInt<HeapIndex>::value) {
375+
return i.value();
376+
} else {
377+
return i;
378+
}
379+
}
288380

289381
// The heap is stored as a vector.
290382
std::vector<Aggregate> data_;
@@ -303,10 +395,10 @@ class AdjustableKAryHeap {
303395
// The number of elements currently in the heap. This may be updated
304396
// either when removing an element (which is not removed from data_), or
305397
// adding a new one.
306-
HeapIndex heap_size_ = 0;
398+
HeapIndex heap_size_ = HeapIndex(0);
307399

308400
// The index for Aggregates not in the heap.
309-
const Index kNonExistent = -1;
401+
const Index kNonExistent = Index(-1);
310402
};
311403

312404
#endif // ORTOOLS_ALGORITHMS_ADJUSTABLE_K_ARY_HEAP_H_

0 commit comments

Comments
 (0)