Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
dense_set.h
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef OR_TOOLS_UTIL_DENSE_SET_H_
15#define OR_TOOLS_UTIL_DENSE_SET_H_
16
17#include <cstddef>
18#include <utility>
19#include <vector>
20
21#include "absl/log/check.h"
22#include "absl/types/span.h"
23
24namespace operations_research {
25// A set of dense non-negative integer values stored in a dense vector.
26//
27// This is useful when we want to iterate over a small subset of the possible
28// values and reuse the memory, or if we want to randomly sample from the set.
29//
30// If the set is usually small but occasionally very large, iterating over a
31// regular hash_set would be less efficient as you would (internal to the hash
32// table iterator) have have to iterate over all the buckets in the hash
33// table even if empty. If you clear the set frequently to avoid this, you would
34// grow and rehash when you have a larger set.
35//
36// If resize=false, users *must* call reserve(K) where K > any key before
37// calling any other method.
38template <typename T, bool auto_resize = true>
39class DenseSet {
40 public:
41 using iterator = typename std::vector<T>::const_iterator;
42 using const_iterator = typename std::vector<T>::const_iterator;
43 using value_type = T;
44 static constexpr bool kAutoResize = auto_resize;
45
46 const_iterator begin() const { return values_.begin(); }
47 const_iterator end() const { return values_.end(); }
48 size_t size() const { return values_.size(); }
49 bool empty() const { return values_.empty(); }
50 void reserve(size_t size) {
51 values_.reserve(size);
52 if (size >= positions_.size()) positions_.resize(size, -1);
53 }
54 size_t capacity() const { return positions_.size(); }
55
56 std::pair<iterator, bool> insert(T value) {
57 const int pos = Position(value);
58 if (pos == -1) {
59 DCHECK_GT(positions_.size(), ToInt(value));
60 positions_[ToInt(value)] = values_.size();
61 values_.push_back(value);
62 return {values_.begin() + positions_[ToInt(value)], true};
63 }
64 return {values_.begin() + pos, false};
65 }
66
68 const int pos = Position(value);
69 DCHECK_GT(positions_.size(), ToInt(value));
70 if (pos < 0) return values_.end();
71 return values_.begin() + pos;
72 }
73
74 bool contains(T value) const {
75 if (kAutoResize && ToInt(value) >= positions_.size()) return false;
76 return positions_[ToInt(value)] >= 0;
77 }
78
79 void erase(iterator it) {
80 const T value = *it;
81 DCHECK_GT(positions_.size(), ToInt(value));
82 positions_[ToInt(values_.back())] = it - values_.begin();
83 positions_[ToInt(value)] = -1;
84 // This is a hack to allow erase to work with a const iterator.
85 values_[it - begin()] = values_.back();
86 values_.pop_back();
87 }
88
89 int erase(T value) {
90 const int pos = Position(value);
91 if (pos < 0) return 0;
92 DCHECK_GT(positions_.size(), ToInt(value));
93 positions_[ToInt(values_.back())] = pos;
94 values_[pos] = values_.back();
95 values_.pop_back();
96 positions_[ToInt(value)] = -1;
97 return 1;
98 }
99
100 // The ordering is deterministic given the same sequence of inserts and
101 // erases but is arbitrary and should not be relied upon.
102 absl::Span<const T> values() const { return values_; }
103
104 void clear() {
105 // We expect values_ to be much smaller than the total number of possible
106 // values, so just clear entries in the set.
107 for (const T value : values_) {
108 DCHECK_GT(positions_.size(), ToInt(value));
109 positions_[ToInt(value)] = -1;
110 }
111 values_.clear();
112 }
113
114 private:
115 static int ToInt(T);
116 inline int Position(T value) {
117 int int_value = ToInt(value);
118 DCHECK_GE(int_value, 0);
119 // Automatic Resize increases the CPU time of microbenchmarks by ~30%, but
120 // even with kAutoResize=true, DenseSet is still 25x faster than a
121 // flat_hash_set<int>.
122 if (kAutoResize && int_value >= positions_.size()) {
123 positions_.resize(ToInt(value) + 1, -1);
124 }
125 DCHECK_GT(positions_.size(), int_value);
126 return positions_[int_value];
127 }
128 std::vector<int> positions_;
129 std::vector<T> values_;
130};
131
132// Like DenseSet, but does not automatically resize the internal position
133// vector, which is ~30% faster.
134template <typename T>
136
137template <typename T, bool resize>
138inline int DenseSet<T, resize>::ToInt(T value) {
139 return value.value();
140}
141template <>
142inline int DenseSet<int, true>::ToInt(int value) {
143 return value;
144}
145template <>
146inline int DenseSet<int, false>::ToInt(int value) {
147 return value;
148}
149
150} // namespace operations_research
151#endif // OR_TOOLS_UTIL_DENSE_SET_H_
typename std::vector< T >::const_iterator const_iterator
Definition dense_set.h:42
const_iterator end() const
Definition dense_set.h:47
const_iterator begin() const
Definition dense_set.h:46
iterator find(T value)
Definition dense_set.h:67
std::pair< iterator, bool > insert(T value)
Definition dense_set.h:56
typename std::vector< T >::const_iterator iterator
Definition dense_set.h:41
static constexpr bool kAutoResize
Definition dense_set.h:44
absl::Span< const T > values() const
Definition dense_set.h:102
void erase(iterator it)
Definition dense_set.h:79
bool contains(T value) const
Definition dense_set.h:74
void reserve(size_t size)
Definition dense_set.h:50
int64_t value
In SWIG mode, we don't want anything besides these top-level includes.