Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions code/data_structures/src/segment_tree/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Segment Tree

A **Segment Tree** is a binary tree used for answering **range queries** (like sum, min, max) and performing **point or range updates** efficiently in logarithmic time.

## Variants

- `segment_tree.cpp`: Basic segment tree supporting range sum queries and point updates.
- `segment_tree_lazy.cpp`: Segment tree with **lazy propagation** to support range updates efficiently.

## Time Complexity

| Operation | Basic Segment Tree | With Lazy Propagation |
|---------------|--------------------|------------------------|
| Build | O(N) | O(N) |
| Point Update | O(log N) | O(log N) |
| Range Query | O(log N) | O(log N) |
| Range Update | N/A | O(log N) |

Where `N` is the size of the input array.

## Sample Usage

```cpp
// Basic segment tree
SegmentTree st(data);
st.update(index, value);
st.query(left, right);

// Segment tree with lazy propagation
SegmentTreeLazy st(data);
st.updateRange(left, right, delta);
st.queryRange(left, right);
83 changes: 83 additions & 0 deletions code/data_structures/src/segment_tree/segment_tree.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include <iostream>
#include <vector>

class SegmentTree {
public:
SegmentTree(const std::vector<int>& data);

void update(int index, int value);
int query(int left, int right);

private:
void build(int node, int start, int end);
void updateUtil(int node, int start, int end, int index, int value);
int queryUtil(int node, int start, int end, int left, int right);

int size_;
std::vector<int> tree_;
std::vector<int> data_;
};

SegmentTree::SegmentTree(const std::vector<int>& data)
: size_{static_cast<int>(data.size())}, tree_(4 * size_), data_{data} {
build(1, 0, size_ - 1);
}

void SegmentTree::build(int node, int start, int end) {
if (start == end) {
tree_[node] = data_[start];
} else {
int mid = (start + end) / 2;
build(2 * node, start, mid);
build(2 * node + 1, mid + 1, end);
tree_[node] = tree_[2 * node] + tree_[2 * node + 1];
}
}

void SegmentTree::update(int index, int value) {
updateUtil(1, 0, size_ - 1, index, value);
}

void SegmentTree::updateUtil(int node, int start, int end, int index, int value) {
if (start == end) {
data_[index] = value;
tree_[node] = value;
} else {
int mid = (start + end) / 2;
if (index <= mid) {
updateUtil(2 * node, start, mid, index, value);
} else {
updateUtil(2 * node + 1, mid + 1, end, index, value);
}
tree_[node] = tree_[2 * node] + tree_[2 * node + 1];
}
}

int SegmentTree::query(int left, int right) {
return queryUtil(1, 0, size_ - 1, left, right);
}

int SegmentTree::queryUtil(int node, int start, int end, int left, int right) {
if (right < start || end < left) {
return 0;
}
if (left <= start && end <= right) {
return tree_[node];
}

int mid = (start + end) / 2;
int sumLeft = queryUtil(2 * node, start, mid, left, right);
int sumRight = queryUtil(2 * node + 1, mid + 1, end, left, right);
return sumLeft + sumRight;
}

int main() {
std::vector<int> data{1, 3, 5, 7, 9, 11};
SegmentTree st(data);

std::cout << "Sum [1, 3]: " << st.query(1, 3) << "\n";
st.update(1, 10);
std::cout << "Sum [1, 3] after update: " << st.query(1, 3) << "\n";

return 0;
}
111 changes: 111 additions & 0 deletions code/data_structures/src/segment_tree/segment_tree_lazy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include <iostream>
#include <vector>

class SegmentTreeLazy {
public:
SegmentTreeLazy(const std::vector<int>& data);

void updateRange(int l, int r, int val);
int queryRange(int l, int r);

private:
void build(int node, int start, int end);
void updateRangeUtil(int node, int start, int end, int l, int r, int val);
int queryRangeUtil(int node, int start, int end, int l, int r);

int size_;
std::vector<int> tree_;
std::vector<int> lazy_;
std::vector<int> data_;
};

SegmentTreeLazy::SegmentTreeLazy(const std::vector<int>& data)
: size_{static_cast<int>(data.size())},
tree_(4 * size_),
lazy_(4 * size_, 0),
data_{data} {
build(1, 0, size_ - 1);
}

void SegmentTreeLazy::build(int node, int start, int end) {
if (start == end) {
tree_[node] = data_[start];
} else {
int mid = (start + end) / 2;
build(2 * node, start, mid);
build(2 * node + 1, mid + 1, end);
tree_[node] = tree_[2 * node] + tree_[2 * node + 1];
}
}

void SegmentTreeLazy::updateRange(int l, int r, int val) {
updateRangeUtil(1, 0, size_ - 1, l, r, val);
}

void SegmentTreeLazy::updateRangeUtil(int node, int start, int end, int l, int r, int val) {
if (lazy_[node] != 0) {
tree_[node] += (end - start + 1) * lazy_[node];
if (start != end) {
lazy_[2 * node] += lazy_[node];
lazy_[2 * node + 1] += lazy_[node];
}
lazy_[node] = 0;
}

if (start > end || start > r || end < l) {
return;
}

if (start >= l && end <= r) {
tree_[node] += (end - start + 1) * val;
if (start != end) {
lazy_[2 * node] += val;
lazy_[2 * node + 1] += val;
}
return;
}

int mid = (start + end) / 2;
updateRangeUtil(2 * node, start, mid, l, r, val);
updateRangeUtil(2 * node + 1, mid + 1, end, l, r, val);
tree_[node] = tree_[2 * node] + tree_[2 * node + 1];
}

int SegmentTreeLazy::queryRange(int l, int r) {
return queryRangeUtil(1, 0, size_ - 1, l, r);
}

int SegmentTreeLazy::queryRangeUtil(int node, int start, int end, int l, int r) {
if (start > end || start > r || end < l) {
return 0;
}

if (lazy_[node] != 0) {
tree_[node] += (end - start + 1) * lazy_[node];
if (start != end) {
lazy_[2 * node] += lazy_[node];
lazy_[2 * node + 1] += lazy_[node];
}
lazy_[node] = 0;
}

if (start >= l && end <= r) {
return tree_[node];
}

int mid = (start + end) / 2;
int p1 = queryRangeUtil(2 * node, start, mid, l, r);
int p2 = queryRangeUtil(2 * node + 1, mid + 1, end, l, r);
return p1 + p2;
}

int main() {
std::vector<int> data{1, 3, 5, 7, 9, 11};
SegmentTreeLazy st(data);

std::cout << "Sum [1, 3]: " << st.queryRange(1, 3) << "\n";
st.updateRange(1, 5, 10);
std::cout << "Sum [1, 3] after update: " << st.queryRange(1, 3) << "\n";

return 0;
}
30 changes: 30 additions & 0 deletions code/data_structures/src/trie/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Trie (Prefix Tree)

A **Trie** is a tree-based data structure that stores a dynamic set or associative array where the keys are usually strings. It is efficient for **searching, inserting, and prefix queries**.

## Operations

- **Insert** a string into the Trie.
- **Search** to check if a string is present.
- **StartsWith** to check if a given prefix exists.

## Time Complexity

| Operation | Complexity |
|-------------|------------|
| Insert | O(L) |
| Search | O(L) |
| StartsWith | O(L) |

Where `L` is the length of the string.

## Sample Usage

```cpp
Trie trie;
trie.insert("apple");
trie.search("apple"); // returns true
trie.search("app"); // returns false
trie.startsWith("app"); // returns true
trie.insert("app");
trie.search("app"); // returns true
73 changes: 73 additions & 0 deletions code/data_structures/src/trie/trie.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <iostream>
#include <string>
#include <array>

class TrieNode {
public:
std::array<TrieNode*, 26> children;
bool isEnd;

TrieNode() : isEnd{false} {
children.fill(nullptr);
}
};

class Trie {
public:
Trie() : root_{new TrieNode()} { }

void insert(const std::string& word);
bool search(const std::string& word);
bool startsWith(const std::string& prefix);

private:
TrieNode* root_;
};

void Trie::insert(const std::string& word) {
TrieNode* node = root_;
for (char ch : word) {
int i = ch - 'a';
if (!node->children[i]) {
node->children[i] = new TrieNode();
}
node = node->children[i];
}
node->isEnd = true;
}

bool Trie::search(const std::string& word) {
TrieNode* node = root_;
for (char ch : word) {
int i = ch - 'a';
if (!node->children[i]) {
return false;
}
node = node->children[i];
}
return node->isEnd;
}

bool Trie::startsWith(const std::string& prefix) {
TrieNode* node = root_;
for (char ch : prefix) {
int i = ch - 'a';
if (!node->children[i]) {
return false;
}
node = node->children[i];
}
return true;
}

int main() {
Trie trie;
trie.insert("apple");
std::cout << "Search 'apple': " << trie.search("apple") << "\n";
std::cout << "Search 'app': " << trie.search("app") << "\n";
std::cout << "StartsWith 'app': " << trie.startsWith("app") << "\n";
trie.insert("app");
std::cout << "Search 'app': " << trie.search("app") << "\n";

return 0;
}