Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
fixed_shape_binary_tree.h
Go to the documentation of this file.
1// Copyright 2010-2025 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_FIXED_SHAPE_BINARY_TREE_H_
15#define OR_TOOLS_UTIL_FIXED_SHAPE_BINARY_TREE_H_
16
17#include <algorithm>
18#include <utility>
19
20#include "absl/log/check.h"
21#include "absl/numeric/bits.h"
23
24namespace operations_research {
25
28
29// An abstract representation of a binary tree that can hold integers in the
30// range [0, num_leaves - 1] and has a depth of exactly
31// 1+ceil(log2(num_leaves)). For example, a FixedShapeBinaryTree(5)
32// can be represented by:
33// [0, 4]
34// / \
35// / \
36// / \
37// [0, 3] [4, 4]
38// / \ / \
39// / \ / \
40// [0, 1] [2, 3] [4, 4] [-1, -1]
41// / \ / \ / \ / \
42// 0 1 2 3 4 -1 -1 -1
43//
44// The most common use of this class is to have a concrete binary tree by
45// defining its storage like:
46// StrongVector<TreeNodeIndex, Val> tree(abstract_tree.StorageSize());
47//
48// Besides the classical binary tree structure of left and right children, this
49// class provides an API to inspect and search the intermediate nodes by their
50// interval values.
52 public:
53 explicit FixedShapeBinaryTree(LeafIndex num_leaves)
54 : largest_leaf_index_(num_leaves - 1) {
55 max_depth_ = absl::bit_width(
56 static_cast<unsigned int>(2 * largest_leaf_index_.value() + 1));
57 leave_start_index_ = 1 << (max_depth_ - 1);
58 }
59
60 int StorageSize() const { return HighestNodeIndex().value() + 1; }
61
62 // If you want to use a different storage for intermediate nodes and leaves.
63 TreeNodeIndex HighestIntermediateNodeIndex() const {
64 return leave_start_index_ - 1;
65 }
66
67 TreeNodeIndex HighestNodeIndex() const { return LastLeafNode(); }
68
69 bool IsLeaf(TreeNodeIndex node) const { return node >= leave_start_index_; }
70
71 TreeNodeIndex Root() const { return TreeNodeIndex(1); }
72
73 TreeNodeIndex FirstLeafNode() const {
74 return TreeNodeIndex(leave_start_index_);
75 }
76
77 TreeNodeIndex LastLeafNode() const {
78 return leave_start_index_ + largest_leaf_index_.value();
79 }
80
81 TreeNodeIndex LeftChild(TreeNodeIndex node) const {
82 DCHECK(!IsLeaf(node));
83 return TreeNodeIndex(node.value() << 1);
84 }
85
86 TreeNodeIndex RightChild(TreeNodeIndex node) const {
87 DCHECK(!IsLeaf(node));
88 return TreeNodeIndex(node.value() << 1) + TreeNodeIndex(1);
89 }
90
91 TreeNodeIndex Parent(TreeNodeIndex node) const {
92 DCHECK_NE(node, Root());
93 return TreeNodeIndex(node.value() >> 1);
94 }
95
96 TreeNodeIndex Sibling(TreeNodeIndex node) const {
97 DCHECK_NE(node, Root());
98 return TreeNodeIndex(node.value() ^ 1);
99 }
100
101 LeafIndex LeafValue(TreeNodeIndex node) const {
102 const LeafIndex ret = LeafIndex((node - leave_start_index_).value());
103 if (ret > largest_leaf_index_) {
104 return LeafIndex(-1);
105 }
106 return ret;
107 }
108
109 // Zero for the root.
110 int Depth(TreeNodeIndex node) const {
111 return absl::bit_width(static_cast<unsigned int>(node.value())) - 1;
112 }
113
114 // Will return [0, num_leaves - 1] for the root, [x, x] for a leaf with x
115 // and the range of all the descendants of a node for intermediate nodes.
116 std::pair<LeafIndex, LeafIndex> GetInterval(TreeNodeIndex node) const {
117 if (IsLeaf(node)) {
118 const LeafIndex leaf_value = LeafValue(node);
119 return {leaf_value, leaf_value};
120 }
121 const int depth = Depth(node);
122 const int pos = node.value() - (1 << depth);
123 const int min = pos << (max_depth_ - depth - 1);
124 if (min > largest_leaf_index_) {
125 return {LeafIndex(-1), LeafIndex(-1)};
126 }
127 const int max = ((pos + 1) << (max_depth_ - depth - 1)) - 1;
128 return {LeafIndex(min),
129 LeafIndex(std::min(max, largest_leaf_index_.value()))};
130 }
131
132 // Given a range of leaf indexes [first_leaf, last_leaf], return the largest
133 // node in the tree associated to an interval [int_begin, int_end] that
134 // satisfies:
135 // - int_begin == first_leaf
136 // - int_end <= last_leaf.
137 // For example, GetNodeStartOfRange(0, num_leaves - 1) = Root().
138 //
139 // This corresponds to a starting node to do a DFS traversal (including all
140 // its children) to cover all intervals fully contained in the range [begin,
141 // end].
142 TreeNodeIndex GetNodeStartOfRange(LeafIndex first_leaf,
143 LeafIndex last_leaf) const {
144 DCHECK_LE(first_leaf, last_leaf);
145 DCHECK_GE(first_leaf, 0);
146 DCHECK_LE(last_leaf, largest_leaf_index_);
147
148 if (last_leaf == largest_leaf_index_) {
149 // Since we truncate the intervals to the largest_leaf_index_, this is
150 // equivalent on the full binary tree to look for the largest possible
151 // value.
152 last_leaf = (1 << (max_depth_ - 1)) - 1;
153 }
154 if (first_leaf == last_leaf) {
155 return GetLeaf(first_leaf);
156 }
157
158 // To see how high we can go on the tree we need to check the two rules:
159 // - we need to start at `first_leaf`, so we need to know which power of two
160 // divides `first_leaf` (odd are leaves, divisible by 2 but not by 4 are
161 // right above the leaves, etc).
162 // - the interval needs to be not greater than `last_leaf - first_leaf`. If
163 // `last_leaf - first_leaf` is zero it must be a leaf, if it is one it can
164 // be one step above, etc).
165 const int power_of_two_div =
166 absl::countr_zero(static_cast<unsigned int>(first_leaf.value()));
167 const int log2_size = absl::bit_width(static_cast<unsigned int>(
168 last_leaf.value() - first_leaf.value() + 1)) -
169 1;
170 const int height = std::min(log2_size, power_of_two_div);
171 const int pos = first_leaf.value() >> height;
172 const int depth = max_depth_ - height - 1;
173 TreeNodeIndex start;
174 start = (1 << depth) + pos;
175 return start;
176 }
177
178 // Given a range of values, return the largest node in the tree associated to
179 // an interval [int_begin, int_end] that satisfies:
180 // - int_end == first_leaf
181 // - int_begin >= last_leaf.
182 // For example, GetNodeEndOfRange(0, largest_leaf_index) = Root().
183 //
184 // This corresponds to a last node (including all its descendants) to do a DFS
185 // traversal to cover all intervals fully contained in the range [begin, end].
186 TreeNodeIndex GetNodeEndOfRange(LeafIndex first_leaf,
187 LeafIndex last_leaf) const {
188 DCHECK_LT(first_leaf, last_leaf);
189 DCHECK_GE(first_leaf, 0);
190 DCHECK_LE(last_leaf, largest_leaf_index_);
191
192 if (first_leaf == last_leaf) {
193 return GetLeaf(first_leaf);
194 }
195
196 // To see how high we can go on the tree we need to check the two rules:
197 // - we need to end at `last_leaf`, so we need to know which power of two
198 // divides `last_leaf+1`.
199 // - the interval needs to be not greater than `last_leaf - first_leaf`. If
200 // `last_leaf - first_leaf` is zero it must be a leaf, if it is one it can
201 // be one step
202 // above, etc).
203 const int log2_size = absl::bit_width(static_cast<unsigned int>(
204 last_leaf.value() - first_leaf.value() + 1)) -
205 1;
206 const int power_of_two_div =
207 absl::countr_zero(static_cast<unsigned int>(last_leaf.value() + 1));
208 const int height = std::min(log2_size, power_of_two_div);
209 const int pos = last_leaf.value() >> height;
210 const int depth = max_depth_ - height - 1;
211 TreeNodeIndex int_end;
212 int_end = (1 << depth) + pos;
213 return int_end;
214 }
215
216 // Given an interval [first_leaf, last_leaf], return O(log n) ordered disjoint
217 // nodes of the tree that cover the interval. The time complexity is O(log n).
218 template <typename TypeWithPushBack>
219 void PartitionIntervalIntoNodes(LeafIndex first_leaf, LeafIndex last_leaf,
220 TypeWithPushBack* result) const {
221 DCHECK_LE(first_leaf, last_leaf);
222 TreeNodeIndex prev(0);
223 TreeNodeIndex current = GetNodeStartOfRange(first_leaf, last_leaf);
224 if (current == Root()) {
225 result->push_back(current);
226 return;
227 }
228 while (true) {
229 const auto& [min, max] = GetInterval(current);
230 if (min >= first_leaf && max <= last_leaf) {
231 result->push_back(current);
232 if (max == last_leaf) {
233 return;
234 }
235 prev = current;
236 current = Parent(current);
237 continue;
238 }
239 if (prev == TreeNodeIndex(0)) {
240 prev = current;
241 current = Parent(current);
242 } else if (prev != Root() && current == Parent(prev)) {
243 // We just moved up.
244 if (prev == LeftChild(current)) {
245 prev = current;
246 current = RightChild(current);
247 } else {
248 DCHECK_EQ(prev, RightChild(current));
249 prev = current;
250 current = Parent(current);
251 }
252 } else {
253 // We just moved down.
254 if (IsLeaf(current)) {
255 prev = current;
256 current = Parent(current);
257 } else {
258 DCHECK_EQ(prev, Parent(current));
259 prev = current;
260 current = LeftChild(current);
261 }
262 }
263 }
264 }
265
266 TreeNodeIndex GetLeaf(LeafIndex value) const {
267 return leave_start_index_ + value.value();
268 }
269
270 private:
271 TreeNodeIndex leave_start_index_;
272 LeafIndex largest_leaf_index_;
273 int max_depth_;
274};
275
276} // namespace operations_research
277
278#endif // OR_TOOLS_UTIL_FIXED_SHAPE_BINARY_TREE_H_
TreeNodeIndex GetNodeEndOfRange(LeafIndex first_leaf, LeafIndex last_leaf) const
int Depth(TreeNodeIndex node) const
Zero for the root.
TreeNodeIndex Parent(TreeNodeIndex node) const
void PartitionIntervalIntoNodes(LeafIndex first_leaf, LeafIndex last_leaf, TypeWithPushBack *result) const
TreeNodeIndex GetLeaf(LeafIndex value) const
TreeNodeIndex Sibling(TreeNodeIndex node) const
TreeNodeIndex RightChild(TreeNodeIndex node) const
TreeNodeIndex GetNodeStartOfRange(LeafIndex first_leaf, LeafIndex last_leaf) const
TreeNodeIndex HighestIntermediateNodeIndex() const
If you want to use a different storage for intermediate nodes and leaves.
TreeNodeIndex LeftChild(TreeNodeIndex node) const
std::pair< LeafIndex, LeafIndex > GetInterval(TreeNodeIndex node) const
LeafIndex LeafValue(TreeNodeIndex node) const
In SWIG mode, we don't want anything besides these top-level includes.
#define DEFINE_STRONG_INDEX_TYPE(index_type_name)