Skip to content

Commit

Permalink
Create Dynamic Segment Trees.md
Browse files Browse the repository at this point in the history
  • Loading branch information
AnushkaChouhan25 committed Oct 30, 2024
1 parent c71858b commit 98191a9
Showing 1 changed file with 169 additions and 0 deletions.
169 changes: 169 additions & 0 deletions docs/Segment-Trees/Dynamic Segment Trees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
id: dynamic-segment-tree
sidebar_position: 13
title: Dynamic Segment Tree
sidebar_label: Dynamic Segment Tree
description: "This post covers dynamic segment trees, their use-cases, code examples, and how they differ from regular segment trees."
tags: [dsa, segment tree, range queries, recursion, c++]
---

# Dynamic Segment Tree

In this post, we’ll cover **Dynamic Segment Trees**, a data structure that extends the capabilities of static segment trees to handle larger ranges efficiently. This is especially useful when the input size is too large to fit into memory at once, or when dealing with dynamic ranges that are sparsely populated.

---

## Problem Definition

A **Dynamic Segment Tree** is a type of segment tree where nodes are created **on-demand** during updates. It allows us to efficiently perform range queries and updates for very large ranges, without pre-allocating memory for all nodes.

### Example Use Case
Suppose you need to manage a large range of values, such as from `1` to `10^9`, but only a small fraction of the elements within the range are relevant. A **dynamic segment tree** only creates the necessary nodes, making it space-efficient.

---

## Approach

The idea behind a dynamic segment tree is to **lazily initialize** nodes as they are accessed during updates or queries. Instead of building the entire tree upfront, we create only the nodes that are necessary.

### Key Characteristics:
1. **Sparse Representation:** Nodes are created on-demand to save space.
2. **Recursive Structure:** Similar to a regular segment tree but with dynamic memory allocation.
3. **Memory Efficient:** Ideal for problems with large ranges but sparse data.

---

## Dynamic Segment Tree Operations

### 1. **Node Structure**

```cpp
struct Node {
int value; // Stores the sum or relevant data for the range
Node* left; // Pointer to the left child
Node* right; // Pointer to the right child

Node(int v = 0) : value(v), left(NULL), right(NULL) {}
};

```

### 2. **Update Operation**

```
void update(Node*& node, int start, int end, int idx, int val) {
if (!node) node = new Node(); // Create node if it doesn't exist
if (start == end) {
node->value += val; // Update the value at the leaf node
return;
}
int mid = (start + end) / 2;
if (idx <= mid)
update(node->left, start, mid, idx, val);
else
update(node->right, mid + 1, end, idx, val);
// Update the current node value based on children
node->value = (node->left ? node->left->value : 0) +
(node->right ? node->right->value : 0);
}
```
### 3. **Range Query Operation**

```
int query(Node* node, int start, int end, int l, int r) {
if (!node || l > end || r < start)
return 0; // Return 0 if out of range or node doesn't exist
if (l <= start && end <= r)
return node->value; // Return value if the current segment is fully in range
int mid = (start + end) / 2;
int leftQuery = query(node->left, start, mid, l, r);
int rightQuery = query(node->right, mid + 1, end, l, r);
return leftQuery + rightQuery;
}
```

## Example Usage
### Code Implementation
```
#include <iostream>
using namespace std;
struct Node {
int value;
Node* left;
Node* right;
Node(int v = 0) : value(v), left(NULL), right(NULL) {}
};
void update(Node*& node, int start, int end, int idx, int val) {
if (!node) node = new Node();
if (start == end) {
node->value += val;
return;
}
int mid = (start + end) / 2;
if (idx <= mid)
update(node->left, start, mid, idx, val);
else
update(node->right, mid + 1, end, idx, val);
node->value = (node->left ? node->left->value : 0) +
(node->right ? node->right->value : 0);
}
int query(Node* node, int start, int end, int l, int r) {
if (!node || l > end || r < start) return 0;
if (l <= start && end <= r) return node->value;
int mid = (start + end) / 2;
int leftQuery = query(node->left, start, mid, l, r);
int rightQuery = query(node->right, mid + 1, end, l, r);
return leftQuery + rightQuery;
}
int main() {
Node* root = NULL; // Initialize an empty segment tree
// Update some elements
update(root, 0, 9, 2, 5);
update(root, 0, 9, 4, 10);
// Perform range queries
cout << "Sum in range [0, 5]: " << query(root, 0, 9, 0, 5) << endl;
cout << "Sum in range [3, 8]: " << query(root, 0, 9, 3, 8) << endl;
return 0;
}
```
### Output
```
Sum in range [0, 5]: 15
Sum in range [3, 8]: 10
```

## Time Complexity

- **Update Operation:** `O(log N)` in the average case.
- **Query Operation:** `O(log N)` in the average case.
- **Space Complexity:** Depends on the number of nodes created, which is proportional to the number of updates.

---

## When to Use a Dynamic Segment Tree

- **Very large ranges:** When the range size is too large to pre-allocate.
- **Sparse updates:** When only a few elements in the range are updated.
- **Memory constraints:** When space efficiency is critical.

---

## Conclusion

Dynamic Segment Trees are a powerful variation of segment trees, useful in scenarios with large but sparse datasets. They provide the same query and update performance as regular segment trees, but with improved memory efficiency by allocating nodes on demand.

0 comments on commit 98191a9

Please sign in to comment.