implement segment tree

This commit is contained in:
Maximilian Keßler 2022-04-16 11:34:29 +02:00
parent 402f860bbc
commit a145dcc108
5 changed files with 169 additions and 24 deletions

View file

@ -5,6 +5,6 @@ set(CMAKE_CXX_STANDARD 20)
find_package(benchmark REQUIRED)
add_executable(prog main.cpp segment_tree.h geometry.h)
add_executable(prog main.cpp segment_tree.h geometry.h segment_tree.cpp)
target_link_libraries(prog benchmark::benchmark)

View file

@ -21,4 +21,22 @@ struct Interval {
Coordinate right;
};
struct Rectangle {
Point bottom_left;
Point top_right;
// only for algorithm
Index i_left;
Index i_right;
};
struct RectCoord {
Coordinate coord;
Rectangle* rect; // guaranteed to be valid at all times
bool operator<(const RectCoord &other) {
return coord < other.coord;
}
};
#endif //PROG_GEOMETRY_H

View file

@ -5,22 +5,6 @@
#include "geometry.h"
struct Rectangle {
Point bottom_left;
Point top_right;
// only for algorithm
Index i_left;
Index i_right;
};
struct RectCoord {
Coordinate coord;
Rectangle* rect; // guaranteed to be valid at all times
bool operator<(const RectCoord &other) {
return coord < other.coord;
}
};
Unit get_area_union(std::vector<Rectangle> rectangles) {
// do some input sanity checks

64
segment_tree.cpp Normal file
View file

@ -0,0 +1,64 @@
//
// Created by maximilian on 16.04.22.
//
#include <assert.h>
#include <bit>
#include "segment_tree.h"
SegmentTree::SegmentTree(const std::vector<RectCoord> &coords):
_covered_cross_section_length(0), _nodes() {
assert(!coords.empty());
// reserve nodes for a full binary tree with at least as many leaves as intervals
size_t num_leaf_nodes = coords.size() -1;
size_t num_meta_nodes = std::bit_ceil(num_leaf_nodes) - 1;
// Initialize all nodes with zero coverage and dummy interval
_nodes.resize(2 * num_meta_nodes - 1, {0, coords[num_leaf_nodes].coord, coords[num_leaf_nodes].coord});
// We initialize the tree from bottom up, keeping track of the index ranges
// and the length of the corresponding segment
for(Index leaf_node_idx = 0 ; leaf_node_idx < num_leaf_nodes ; ++leaf_node_idx) {
_nodes[leaf_node_idx + num_meta_nodes].left_coord = coords[leaf_node_idx].coord;
_nodes[leaf_node_idx + num_meta_nodes].right_coord = coords[leaf_node_idx +1].coord;
}
// Note that there are remaining leafs that represent dummy ranges
for(Index node_idx = num_meta_nodes - 1; node_idx >= 0; ++node_idx) {
_nodes[node_idx].left_coord = left_child(node_idx).left_coord;
_nodes[node_idx].right_coord = right_child(node_idx).right_coord;
}
assert(_nodes.front().left_coord == coords.front().coord);
assert(_nodes.front().right_coord == coords.back().coord);
assert(_nodes.front().segment_length() == coords.back().coord - coords.front().coord);
}
void SegmentTree::add_interval(Interval interval, Index node_idx) {
if (interval.left <= _nodes[node_idx].left_coord && _nodes[node_idx].right_coord <= interval.right) {
update_added(node_idx);
} else {
if(interval.left < left_child(node_idx).right_coord) {
add_interval(interval, left_child_idx(node_idx));
}
if(right_child(node_idx).left_coord < interval.right) {
add_interval(interval, right_child_idx(node_idx));
}
}
}
void SegmentTree::remove_interval(Interval interval, Index node_idx) {
if (interval.left <= _nodes[node_idx].left_coord && _nodes[node_idx].right_coord <= interval.right) {
update_removed(node_idx);
} else {
if(interval.left < left_child(node_idx).right_coord) {
remove_interval(interval, left_child_idx(node_idx));
}
if(right_child(node_idx).left_coord < interval.right) {
remove_interval(interval, right_child_idx(node_idx));
}
}
}

View file

@ -9,13 +9,92 @@
#define PROG_SEGMENT_TREE_H
class SegmentTree {
SegmentTree(const std::vector<Coordinate>);
Unit length_covered_intervals();
void add_interval(Interval interval);
void remove_interval(Interval interval);
struct TreeNode {
unsigned coverage;
Coordinate left_coord;
Coordinate right_coord;
inline Unit segment_length();
};
class SegmentTree {
std::vector<TreeNode> _nodes;
Unit _covered_cross_section_length;
public:
/**
*
* @param coords Sorted vector of coordinates
*/
SegmentTree(const std::vector<RectCoord>& coords);
inline Unit length_covered_intervals();
inline void add_interval(Interval interval);
inline void remove_interval(Interval interval);
private:
inline static Index left_child_idx(Index node_idx);
inline static Index right_child_idx(Index node_idx);
inline TreeNode& left_child(Index node_idx);
inline TreeNode& right_child(Index node_idx);
inline void update_added(Index node_idx);
inline void update_removed(Index node_idx);
void add_interval(Interval interval, Index node_idx);
void remove_interval(Interval interval, Index node_idx);
};
/*****************************************************
* INLINE section
****************************************************/
TreeNode& SegmentTree::left_child(Index node_idx) {
return _nodes[left_child_idx(node_idx)];
}
TreeNode& SegmentTree::right_child(Index node_idx) {
return _nodes[right_child_idx(node_idx)];
}
Unit SegmentTree::length_covered_intervals() {
return _covered_cross_section_length;
}
void SegmentTree::add_interval(Interval interval) {
add_interval(interval, 0);
}
void SegmentTree::remove_interval(Interval interval) {
remove_interval(interval, 0);
}
void SegmentTree::update_added(Index node_idx) {
++(_nodes[node_idx].coverage);
if(_nodes[node_idx].coverage == 1) {
_covered_cross_section_length += _nodes[node_idx].segment_length();
}
}
void SegmentTree::update_removed(Index node_idx) {
--(_nodes[node_idx].coverage);
if(_nodes[node_idx].coverage == 0) {
_covered_cross_section_length -= _nodes[node_idx].segment_length();
}
}
Index SegmentTree::left_child_idx(Index node_idx) {
return 2*node_idx + 1;
}
Index SegmentTree::right_child_idx(Index node_idx) {
return 2*node_idx + 2;
}
Unit TreeNode::segment_length() {
return right_coord - left_coord;
}
#endif //PROG_SEGMENT_TREE_H