0% found this document useful (0 votes)
2 views28 pages

Essential Algorithms Reference Guide

The document discusses various dynamic programming patterns and algorithms, including the 0/1 Knapsack Problem, Longest Common Subsequence, and Edit Distance, providing problem statements, examples, and C++ implementations. It also covers data structures and algorithms such as Disjoint Set Union, Segment Trees, Dijkstra's Algorithm, Binary Indexed Trees, Tries, KMP String Matching, and Binary Search variations. Each section includes use cases, time complexities, and code snippets for practical understanding.

Uploaded by

suryansh.ug22
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views28 pages

Essential Algorithms Reference Guide

The document discusses various dynamic programming patterns and algorithms, including the 0/1 Knapsack Problem, Longest Common Subsequence, and Edit Distance, providing problem statements, examples, and C++ implementations. It also covers data structures and algorithms such as Disjoint Set Union, Segment Trees, Dijkstra's Algorithm, Binary Indexed Trees, Tries, KMP String Matching, and Binary Search variations. Each section includes use cases, time complexities, and code snippets for practical understanding.

Uploaded by

suryansh.ug22
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Dynamic Programming Patterns

Dynamic Programming solves problems by breaking them into overlapping subproblems and storing
results to avoid recomputation.

0/1 Knapsack Problem


Problem Statement: You have a knapsack with capacity W and n items. Each item has weight w[i] and
value v[i]. Maximize value without exceeding weight capacity. Each item can be taken at most once.

Example:

Weights: [1, 3, 4, 5], Values: [1, 4, 5, 7], Capacity: 7


Optimal: Take items with weights 3 and 4 (values 4 + 5 = 9)

DP State: dp[i][w] = maximum value using first i items with weight limit w

Recurrence:

If weight[i-1] > w: dp[i][w] = dp[i-1][w] (can't include item)


Else: dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight[i-1]] + value[i-1])

cpp

// Space optimized version (O(W) space instead of O(nW))


int knapsackOptimized(vector<int>& weights, vector<int>& values, int capacity) {
vector<int> dp(capacity + 1, 0);

for(int i = 0; i < [Link](); i++) {


// Process backwards to avoid using updated values
for(int w = capacity; w >= weights[i]; w--) {
dp[w] = max(dp[w], dp[w - weights[i]] + values[i]);
}
}

return dp[capacity];
}

Longest Common Subsequence (LCS)


Problem Statement: Given two strings, find the length of their longest common subsequence. A
subsequence is derived by deleting some characters without changing the order of remaining characters.

Example:

Input: text1 = "abcde", text2 = "ace"


Output: 3 (LCS is "ace")

DP State: dp[i][j] = LCS length of text1[0...i-1] and text2[0...j-1]

Recurrence:

If text1[i-1] == text2[j-1]: dp[i][j] = dp[i-1][j-1] + 1

Else: dp[i][j] = max(dp[i-1][j], dp[i][j-1])

cpp

// Also returns the actual LCS string


pair<int, string> longestCommonSubsequenceWithString(string text1, string text2) {
int m = [Link](), n = [Link]();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

for(int i = 1; i <= m; i++) {


for(int j = 1; j <= n; j++) {
if(text1[i-1] == text2[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}

// Reconstruct LCS string


string lcs = "";
int i = m, j = n;
while(i > 0 && j > 0) {
if(text1[i-1] == text2[j-1]) {
lcs = text1[i-1] + lcs;
i--; j--;
} else if(dp[i-1][j] > dp[i][j-1]) {
i--;
} else {
j--;
}
}

return {dp[m][n], lcs};


}

Edit Distance (Levenshtein Distance)


Problem Statement: Given two strings word1 and word2,# Essential Algorithms Reference Guide A
comprehensive guide to important algorithms with C++ implementations
Table of Contents
1. Disjoint Set Union (DSU)

2. Segment Tree

3. Dijkstra's Algorithm
4. Binary Indexed Tree (Fenwick Tree)

5. Trie Data Structure


6. KMP String Matching

7. Binary Search Variations

8. Graph Algorithms

9. Dynamic Programming Patterns

10. Mathematical Algorithms

Disjoint Set Union (DSU)


Use Cases: Connected components, cycle detection, Kruskal's MST, dynamic connectivity

Time Complexity: O(α(n)) per operation (nearly constant)

Implementation

cpp
class DSU {
vector<int> parent, rank;
public:
DSU(int n) {
[Link](n);
[Link](n, 0);
for(int i = 0; i < n; i++) parent[i] = i;
}

int find(int x) {
if(parent[x] != x)
parent[x] = find(parent[x]); // Path compression
return parent[x];
}

bool unite(int x, int y) {


int px = find(x), py = find(y);
if(px == py) return false;

// Union by rank
if(rank[px] < rank[py]) swap(px, py);
parent[py] = px;
if(rank[px] == rank[py]) rank[px]++;
return true;
}

bool connected(int x, int y) {


return find(x) == find(y);
}
};

Example Problem: Number of Connected Components

cpp
int countComponents(int n, vector<vector<int>>& edges) {
DSU dsu(n);
for(auto& edge : edges) {
[Link](edge[0], edge[1]);
}

int components = 0;
for(int i = 0; i < n; i++) {
if([Link](i) == i) components++;
}
return components;
}

Segment Tree
Use Cases: Range queries, range updates, lazy propagation

Time Complexity: O(log n) per query/update, O(n) build

Basic Range Sum Query

cpp
class SegmentTree {
vector<long long> tree;
int n;

void build(vector<int>& arr, int node, int start, int end) {


if(start == end) {
tree[node] = arr[start];
} else {
int mid = (start + end) / 2;
build(arr, 2*node, start, mid);
build(arr, 2*node+1, mid+1, end);
tree[node] = tree[2*node] + tree[2*node+1];
}
}

void update(int node, int start, int end, int idx, int val) {
if(start == end) {
tree[node] = val;
} else {
int mid = (start + end) / 2;
if(idx <= mid)
update(2*node, start, mid, idx, val);
else
update(2*node+1, mid+1, end, idx, val);
tree[node] = tree[2*node] + tree[2*node+1];
}
}

long long query(int node, int start, int end, int l, int r) {
if(r < start || end < l) return 0;
if(l <= start && end <= r) return tree[node];

int mid = (start + end) / 2;


return query(2*node, start, mid, l, r) +
query(2*node+1, mid+1, end, l, r);
}

public:
SegmentTree(vector<int>& arr) {
n = [Link]();
[Link](4 * n);
build(arr, 1, 0, n-1);
}

void update(int idx, int val) { update(1, 0, n-1, idx, val); }


long long query(int l, int r) { return query(1, 0, n-1, l, r); }
};

Lazy Propagation for Range Updates

cpp
class LazySegmentTree {
vector<long long> tree, lazy;
int n;

void push(int node, int start, int end) {


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

void updateRange(int node, int start, int end, int l, int r, int val) {
push(node, start, end);
if(start > r || end < l) return;

if(start >= l && end <= r) {


lazy[node] += val;
push(node, start, end);
return;
}

int mid = (start + end) / 2;


updateRange(2*node, start, mid, l, r, val);
updateRange(2*node+1, mid+1, end, l, r, val);

push(2*node, start, mid);


push(2*node+1, mid+1, end);
tree[node] = tree[2*node] + tree[2*node+1];
}

public:
LazySegmentTree(int size) {
n = size;
[Link](4 * n);
[Link](4 * n);
}

void updateRange(int l, int r, int val) {


updateRange(1, 0, n-1, l, r, val);
}
};
Dijkstra's Algorithm
Use Cases: Shortest path in weighted graphs (non-negative weights)

Time Complexity: O((V + E) log V)

Implementation

cpp

vector<int> dijkstra(int start, vector<vector<pair<int, int>>>& graph) {


int n = [Link]();
vector<int> dist(n, INT_MAX);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;

dist[start] = 0;
[Link]({0, start});

while(![Link]()) {
int d = [Link]().first;
int u = [Link]().second;
[Link]();

if(d > dist[u]) continue;

for(auto& edge : graph[u]) {


int v = [Link];
int w = [Link];

if(dist[u] + w < dist[v]) {


dist[v] = dist[u] + w;
[Link]({dist[v], v});
}
}
}

return dist;
}

Example: Network Delay Time

cpp
int networkDelayTime(vector<vector<int>>& times, int n, int k) {
vector<vector<pair<int, int>>> graph(n + 1);
for(auto& time : times) {
graph[time[0]].push_back({time[1], time[2]});
}

vector<int> dist = dijkstra(k, graph);


int maxTime = 0;
for(int i = 1; i <= n; i++) {
if(dist[i] == INT_MAX) return -1;
maxTime = max(maxTime, dist[i]);
}
return maxTime;
}

Binary Indexed Tree (Fenwick Tree)


Use Cases: Prefix sums, range sum queries with point updates

Time Complexity: O(log n) per operation

Implementation

cpp
class BIT {
vector<long long> tree;
int n;

public:
BIT(int size) {
n = size;
[Link](n + 1, 0);
}

void update(int idx, int val) {


for(int i = idx; i <= n; i += i & (-i)) {
tree[i] += val;
}
}

long long query(int idx) {


long long sum = 0;
for(int i = idx; i > 0; i -= i & (-i)) {
sum += tree[i];
}
return sum;
}

long long rangeQuery(int l, int r) {


return query(r) - query(l - 1);
}
};

Example: Count Inversions

cpp
int countInversions(vector<int>& arr) {
// Coordinate compression
vector<int> sorted = arr;
sort([Link](), [Link]());
[Link](unique([Link](), [Link]()), [Link]());

BIT bit([Link]());
int inversions = 0;

for(int i = [Link]() - 1; i >= 0; i--) {


int pos = lower_bound([Link](), [Link](), arr[i]) - [Link]() + 1;
inversions += [Link](pos - 1);
[Link](pos, 1);
}

return inversions;
}

Trie Data Structure


Use Cases: String prefix matching, autocomplete, word search

Time Complexity: O(m) per operation where m is string length

Implementation

cpp
struct TrieNode {
TrieNode* children[26];
bool isEnd;

TrieNode() {
for(int i = 0; i < 26; i++) children[i] = nullptr;
isEnd = false;
}
};

class Trie {
TrieNode* root;

public:
Trie() { root = new TrieNode(); }

void insert(string word) {


TrieNode* curr = root;
for(char c : word) {
int idx = c - 'a';
if(!curr->children[idx]) {
curr->children[idx] = new TrieNode();
}
curr = curr->children[idx];
}
curr->isEnd = true;
}

bool search(string word) {


TrieNode* curr = root;
for(char c : word) {
int idx = c - 'a';
if(!curr->children[idx]) return false;
curr = curr->children[idx];
}
return curr->isEnd;
}

bool startsWith(string prefix) {


TrieNode* curr = root;
for(char c : prefix) {
int idx = c - 'a';
if(!curr->children[idx]) return false;
curr = curr->children[idx];
}
return true;
}
};

KMP String Matching


Use Cases: Pattern matching, finding all occurrences

Time Complexity: O(n + m)

Implementation

cpp
vector<int> computeLPS(string pattern) {
int m = [Link]();
vector<int> lps(m, 0);
int len = 0, i = 1;

while(i < m) {
if(pattern[i] == pattern[len]) {
len++;
lps[i] = len;
i++;
} else {
if(len != 0) {
len = lps[len - 1];
} else {
lps[i] = 0;
i++;
}
}
}
return lps;
}

vector<int> KMPSearch(string text, string pattern) {


vector<int> result;
vector<int> lps = computeLPS(pattern);

int n = [Link]();
int m = [Link]();
int i = 0, j = 0;

while(i < n) {
if(pattern[j] == text[i]) {
j++; i++;
}

if(j == m) {
result.push_back(i - j);
j = lps[j - 1];
} else if(i < n && pattern[j] != text[i]) {
if(j != 0) {
j = lps[j - 1];
} else {
i++;
}
}
}
return result;
}

Binary Search Variations

Standard Binary Search

cpp

int binarySearch(vector<int>& arr, int target) {


int left = 0, right = [Link]() - 1;

while(left <= right) {


int mid = left + (right - left) / 2;
if(arr[mid] == target) return mid;
else if(arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}

Lower Bound (First occurrence >= target)

cpp

int lowerBound(vector<int>& arr, int target) {


int left = 0, right = [Link]();

while(left < right) {


int mid = left + (right - left) / 2;
if(arr[mid] < target) left = mid + 1;
else right = mid;
}
return left;
}

Upper Bound (First occurrence > target)

cpp
int upperBound(vector<int>& arr, int target) {
int left = 0, right = [Link]();

while(left < right) {


int mid = left + (right - left) / 2;
if(arr[mid] <= target) left = mid + 1;
else right = mid;
}
return left;
}

Graph Algorithms

Graph Algorithms

DFS Applications
Problem 1: Number of Islands Given a 2D grid of '1's (land) and '0's (water), count the number of
islands. An island is surrounded by water and formed by connecting adjacent lands horizontally or
vertically.

Example:

grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
Output: 1

Approach: DFS from each unvisited '1' to mark the entire island.

cpp
class Solution {
void dfs(vector<vector<char>>& grid, int i, int j) {
if(i < 0 || i >= [Link]() || j < 0 || j >= grid[0].size() || grid[i][j] == '0') {
return;
}

grid[i][j] = '0'; // Mark as visited

// Visit all 4 directions


dfs(grid, i+1, j);
dfs(grid, i-1, j);
dfs(grid, i, j+1);
dfs(grid, i, j-1);
}

public:
int numIslands(vector<vector<char>>& grid) {
int islands = 0;

for(int i = 0; i < [Link](); i++) {


for(int j = 0; j < grid[0].size(); j++) {
if(grid[i][j] == '1') {
islands++;
dfs(grid, i, j); // Mark entire island
}
}
}

return islands;
}
};

Problem 2: Detect Cycle in Undirected Graph

cpp
bool hasCycleDFS(int node, int parent, vector<vector<int>>& adj, vector<bool>& visited) {
visited[node] = true;

for(int neighbor : adj[node]) {


if(neighbor == parent) continue; // Skip edge to parent

if(visited[neighbor]) return true; // Back edge found = cycle

if(hasCycleDFS(neighbor, node, adj, visited)) {


return true;
}
}

return false;
}

bool detectCycle(int n, vector<vector<int>>& adj) {


vector<bool> visited(n, false);

for(int i = 0; i < n; i++) {


if(!visited[i]) {
if(hasCycleDFS(i, -1, adj, visited)) {
return true;
}
}
}

return false;
}

BFS Applications
Problem 1: Shortest Path in Unweighted Graph Find shortest distance between two nodes in
unweighted graph.

Example:

Graph: 0-1-2-3, 0-2

Shortest path from 0 to 3: 0→2→3 (distance = 2)

cpp
int shortestPath(int start, int end, vector<vector<int>>& adj) {
if(start == end) return 0;

queue<int> q;
vector<bool> visited([Link](), false);
vector<int> distance([Link](), -1);

[Link](start);
visited[start] = true;
distance[start] = 0;

while(![Link]()) {
int node = [Link]();
[Link]();

for(int neighbor : adj[node]) {


if(!visited[neighbor]) {
visited[neighbor] = true;
distance[neighbor] = distance[node] + 1;
[Link](neighbor);

if(neighbor == end) return distance[neighbor];


}
}
}

return -1; // No path exists


}

Problem 2: Word Ladder Given two words (beginWord and endWord) and a dictionary, find length of
shortest transformation sequence from beginWord to endWord, changing only one letter at a time.

Example:

beginWord = "hit", endWord = "cog"

wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5 ("hit" → "hot" → "dot" → "dog" → "cog")

cpp
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> wordSet([Link](), [Link]());
if([Link](endWord) == [Link]()) return 0;

queue<string> q;
[Link](beginWord);
int level = 1;

while(![Link]()) {
int size = [Link]();

for(int i = 0; i < size; i++) {


string word = [Link]();
[Link]();

if(word == endWord) return level;

// Try changing each character


for(int j = 0; j < [Link](); j++) {
char original = word[j];
for(char c = 'a'; c <= 'z'; c++) {
if(c == original) continue;

word[j] = c;
if([Link](word) != [Link]()) {
[Link](word);
[Link](word); // Mark as visited
}
}
word[j] = original; // Restore
}
}
level++;
}

return 0;
}

Topological Sort Applications


Problem 1: Course Schedule There are numCourses courses labeled 0 to numCourses-1. Some courses
have prerequisites. Return true if you can finish all courses.

Example:

Input: numCourses = 2, prerequisites = [[1,0]]


Output: true (take course 0 first, then course 1)

Approach: If graph has a cycle, impossible to complete all courses.

cpp

bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {


vector<vector<int>> adj(numCourses);
vector<int> indegree(numCourses, 0);

// Build graph
for(auto& prereq : prerequisites) {
adj[prereq[1]].push_back(prereq[0]);
indegree[prereq[0]]++;
}

// Topological sort using Kahn's algorithm


queue<int> q;
for(int i = 0; i < numCourses; i++) {
if(indegree[i] == 0) [Link](i);
}

int completed = 0;
while(![Link]()) {
int course = [Link]();
[Link]();
completed++;

for(int nextCourse : adj[course]) {


indegree[nextCourse]--;
if(indegree[nextCourse] == 0) {
[Link](nextCourse);
}
}
}

return completed == numCourses;


}

Problem 2: Find Order to Complete Courses Return the ordering of courses you should take to finish all
courses.

cpp
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> adj(numCourses);
vector<int> indegree(numCourses, 0);

for(auto& prereq : prerequisites) {


adj[prereq[1]].push_back(prereq[0]);
indegree[prereq[0]]++;
}

queue<int> q;
for(int i = 0; i < numCourses; i++) {
if(indegree[i] == 0) [Link](i);
}

vector<int> order;
while(![Link]()) {
int course = [Link]();
[Link]();
order.push_back(course);

for(int nextCourse : adj[course]) {


indegree[nextCourse]--;
if(indegree[nextCourse] == 0) {
[Link](nextCourse);
}
}
}

return [Link]() == numCourses ? order : vector<int>();


}

Dynamic Programming Patterns

Knapsack (0/1)

cpp
int knapsack(vector<int>& weights, vector<int>& values, int capacity) {
int n = [Link]();
vector<vector<int>> dp(n + 1, vector<int>(capacity + 1, 0));

for(int i = 1; i <= n; i++) {


for(int w = 1; w <= capacity; w++) {
if(weights[i-1] <= w) {
dp[i][w] = max(dp[i-1][w],
dp[i-1][w - weights[i-1]] + values[i-1]);
} else {
dp[i][w] = dp[i-1][w];
}
}
}

return dp[n][capacity];
}

Longest Common Subsequence

cpp

int longestCommonSubsequence(string text1, string text2) {


int m = [Link](), n = [Link]();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

for(int i = 1; i <= m; i++) {


for(int j = 1; j <= n; j++) {
if(text1[i-1] == text2[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}

return dp[m][n];
}

Edit Distance

cpp
int minDistance(string word1, string word2) {
int m = [Link](), n = [Link]();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));

for(int i = 0; i <= m; i++) dp[i][0] = i;


for(int j = 0; j <= n; j++) dp[0][j] = j;

for(int i = 1; i <= m; i++) {


for(int j = 1; j <= n; j++) {
if(word1[i-1] == word2[j-1]) {
dp[i][j] = dp[i-1][j-1];
} else {
dp[i][j] = 1 + min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]});
}
}
}

return dp[m][n];
}

Mathematical Algorithms

Fast Exponentiation

cpp

long long fastPow(long long base, long long exp, long long mod) {
long long result = 1;
base %= mod;

while(exp > 0) {
if(exp & 1) result = (result * base) % mod;
base = (base * base) % mod;
exp >>= 1;
}

return result;
}

GCD and LCM

cpp
long long gcd(long long a, long long b) {
return b == 0 ? a : gcd(b, a % b);
}

long long lcm(long long a, long long b) {


return (a / gcd(a, b)) * b;
}

Sieve of Eratosthenes

cpp

vector<bool> sieveOfEratosthenes(int n) {
vector<bool> isPrime(n + 1, true);
isPrime[0] = isPrime[1] = false;

for(int i = 2; i * i <= n; i++) {


if(isPrime[i]) {
for(int j = i * i; j <= n; j += i) {
isPrime[j] = false;
}
}
}

return isPrime;
}

Matrix Exponentiation

cpp
vector<vector<long long>> multiply(vector<vector<long long>>& A,
vector<vector<long long>>& B, int mod) {
int n = [Link]();
vector<vector<long long>> C(n, vector<long long>(n, 0));

for(int i = 0; i < n; i++) {


for(int j = 0; j < n; j++) {
for(int k = 0; k < n; k++) {
C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod;
}
}
}

return C;
}

vector<vector<long long>> matrixPower(vector<vector<long long>>& matrix,


long long n, int mod) {
int size = [Link]();
vector<vector<long long>> result(size, vector<long long>(size, 0));

// Identity matrix
for(int i = 0; i < size; i++) result[i][i] = 1;

while(n > 0) {
if(n & 1) result = multiply(result, matrix, mod);
matrix = multiply(matrix, matrix, mod);
n >>= 1;
}

return result;
}

Quick Reference

Common Time Complexities


Sorting: O(n log n)

Binary Search: O(log n)

DFS/BFS: O(V + E)
Dijkstra: O((V + E) log V)

Union-Find: O(α(n)) ≈ O(1)


Segment Tree: O(log n) query/update
Trie: O(m) where m is string length

Space Optimization Tips


Use 1D arrays when possible for DP
Coordinate compression for large ranges

Lazy propagation for range updates


Path compression in Union-Find

Common Patterns
Two Pointers: Sorted arrays, palindromes

Sliding Window: Subarray problems


Binary Search: Answer search problems

DP: Optimization problems


Graph: Connectivity, shortest paths

This reference guide covers essential algorithms for competitive programming and technical interviews.
Practice implementing these algorithms and understanding when to apply each technique.

You might also like