This commit is contained in:
MSedra 2021-02-01 00:44:29 +02:00
parent 020c6141c3
commit 4b6566d2f9

View File

@ -1,6 +1,6 @@
/**
* @file
* @brief [Persistent segment tree with range updates (lazy propagation)](https://en.wikipedia.org/wiki/Persistent_data_structure - https://www.geeksforgeeks.org/lazy-propagation-in-segment-tree/)
* @brief [Persistent segment tree with range updates (lazy propagation)](https://en.wikipedia.org/wiki/Persistent_data_structure)
*
* @details
* A normal segment tree facilitates making point updates and range queries in logarithmic time.
@ -15,11 +15,19 @@
*
* @author [Magdy Sedra](https://github.com/MSedra)
*/
#include<iostream>
#include<vector>
#include<iostream> /// for IO operations
#include<vector> /// for std::vector
#include <memory> /// to manage dynamic memory
/// Range query here is range sum ,but the code can be modified to make different queries like range max or min
/**
* @namespace range_queries
* @brief Range queries algorithms
*/
namespace range_queries {
/**
* @brief Range query here is range sum, but the code can be modified to make different queries like range max or min.
*/
class perSegTree {
private:
class Node {
@ -41,7 +49,7 @@ private:
/**
* @brief Creating a new node with the same values of curr node
* @param curr node that would be copied
* @returns the new node
* @return the new node
*/
std::shared_ptr<Node> newKid(std::shared_ptr<Node> const &curr) {
auto newNode = std::make_shared<Node>(Node());
@ -57,9 +65,9 @@ private:
* @param i the left index of the range that the passed node holds its sum
* @param j the right index of the range that the passed node holds its sum
* @param curr pointer to the node to be propagated
* @returns void
* @return void
*/
void lazy(int i, int j, std::shared_ptr<Node> const &curr) {
void lazy(const int &i, const int &j, std::shared_ptr<Node> const &curr) {
if (!curr->prop) {
return;
}
@ -77,9 +85,9 @@ private:
* @brief Constructing the segment tree with the early passed vector. Every call creates a node to hold the sum of the given range, set its pointers to the children, and set its value to the sum of the children's values
* @param i the left index of the range that the created node holds its sum
* @param j the right index of the range that the created node holds its sum
* @returns pointer to the newly created node
* @return pointer to the newly created node
*/
std::shared_ptr<Node> construct(int i, int j) {
std::shared_ptr<Node> construct(const int &i, const int &j) {
auto newNode = std::make_shared<Node>(Node());
if (i == j) {
newNode->val = vec[i];
@ -102,9 +110,9 @@ private:
* @param r the right index of the range to be updated
* @param value the value to be added to every element whose index x satisfies l<=x<=r
* @param curr pointer to the current node, which has value = the sum of elements whose index x satisfies i<=x<=j
* @return
* @return pointer to the current newly created node
*/
std::shared_ptr<Node> update(int i, int j, int l, int r, int value, std::shared_ptr<Node> const &curr) {
std::shared_ptr<Node> update(const int &i, const int &j, const int &l, const int &r, const int &value, std::shared_ptr<Node> const &curr) {
lazy(i, j, curr);
if (i >= l && j <= r) {
std::shared_ptr<Node> newNode = newKid(curr);
@ -132,7 +140,7 @@ private:
* @param curr pointer to the current node, which has value = the sum of elements whose index x satisfies i<=x<=j
* @return sum of elements whose index x satisfies l<=x<=r
*/
int64_t query(int i, int j, int l, int r, std::shared_ptr<Node> const &curr) {
int64_t query(const int &i, const int &j, const int &l, const int &r, std::shared_ptr<Node> const &curr) {
lazy(i, j, curr);
if (j < l || r < i) {
return 0;
@ -143,6 +151,7 @@ private:
int mid = i + (j - i) / 2;
return query(i, mid, l, r, curr->left) + query(mid + 1, j, l, r, curr->right);
}
/**
* public methods that can be used directly from outside the class. They call the private functions that do all the work
*/
@ -154,6 +163,7 @@ public:
/**
* @brief Constructing the segment tree with the values in the passed vector. Returned root pointer is pushed in the pointers vector to have access to the original version if the segment tree is updated
* @param vec vector whose values will be used to build the segment tree
* @return void
*/
void construct(const std::vector<int> &vec) // the segment tree will be built from the values in "vec", "vec" is 0 indexed
{
@ -171,8 +181,9 @@ public:
* @param l the left index of the range to be updated
* @param r the right index of the range to be updated
* @param value the value to be added to every element whose index x satisfies l<=x<=r
* @return void
*/
void update(int l, int r, int value) // all elements from index "l" to index "r" would by updated by "value", "l" and "r" are 0 indexed
void update(const int &l, const int &r, const int &value) // all elements from index "l" to index "r" would by updated by "value", "l" and "r" are 0 indexed
{
ptrs.push_back(update(0, n - 1, l, r, value,ptrs[ptrs.size() - 1])); // saving the root pointer to the new segment tree
}
@ -184,7 +195,7 @@ public:
* @param version the version to query on. If equals to 0, the original segment tree will be queried
* @return sum of elements whose index x satisfies l<=x<=r
*/
int64_t query(int l, int r, int version) // querying the range from "l" to "r" in a segment tree after "version" updates, "l" and "r" are 0 indexed
int64_t query(const int &l, const int &r, const int &version) // querying the range from "l" to "r" in a segment tree after "version" updates, "l" and "r" are 0 indexed
{
return query(0, n - 1, l, r, ptrs[version]);
}
@ -198,10 +209,11 @@ public:
return ptrs.size();
}
};
} // namespace range_queries
void test() {
std::vector<int> arr = {-5, 2, 3, 11, -2, 7, 0, 1};
perSegTree tree;
range_queries::perSegTree tree;
std::cout << "Elements before any updates are {";
for (int i = 0; i < arr.size(); ++i) {
std::cout << arr[i];
@ -238,6 +250,9 @@ void test() {
}
int main() {
test();
test(); // run self-test implementations
return 0;
}