diff --git a/code/data_structures/src/segment_tree/README.md b/code/data_structures/src/segment_tree/README.md new file mode 100644 index 0000000000..aa4f257c52 --- /dev/null +++ b/code/data_structures/src/segment_tree/README.md @@ -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); diff --git a/code/data_structures/src/segment_tree/segment_tree.cpp b/code/data_structures/src/segment_tree/segment_tree.cpp new file mode 100644 index 0000000000..0c83e81f4a --- /dev/null +++ b/code/data_structures/src/segment_tree/segment_tree.cpp @@ -0,0 +1,83 @@ +#include +#include + +class SegmentTree { +public: + SegmentTree(const std::vector& 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 tree_; + std::vector data_; +}; + +SegmentTree::SegmentTree(const std::vector& data) + : size_{static_cast(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 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; +} \ No newline at end of file diff --git a/code/data_structures/src/segment_tree/segment_tree_lazy.cpp b/code/data_structures/src/segment_tree/segment_tree_lazy.cpp new file mode 100644 index 0000000000..0168055ecd --- /dev/null +++ b/code/data_structures/src/segment_tree/segment_tree_lazy.cpp @@ -0,0 +1,111 @@ +#include +#include + +class SegmentTreeLazy { +public: + SegmentTreeLazy(const std::vector& 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 tree_; + std::vector lazy_; + std::vector data_; +}; + +SegmentTreeLazy::SegmentTreeLazy(const std::vector& data) + : size_{static_cast(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 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; +} \ No newline at end of file diff --git a/code/data_structures/src/trie/README.md b/code/data_structures/src/trie/README.md new file mode 100644 index 0000000000..4872e8b72f --- /dev/null +++ b/code/data_structures/src/trie/README.md @@ -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 \ No newline at end of file diff --git a/code/data_structures/src/trie/trie.cpp b/code/data_structures/src/trie/trie.cpp new file mode 100644 index 0000000000..bc6f1c6eb4 --- /dev/null +++ b/code/data_structures/src/trie/trie.cpp @@ -0,0 +1,73 @@ +#include +#include +#include + +class TrieNode { +public: + std::array 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; +} \ No newline at end of file