Example: A graph with 5 vertices.
1 → 2 → 3 → 51 → 2 → 3 → 2 → 1 (node repeats)1 → 3 → 5 (missing edge between 1 and 3)n (nodes) and m (edges).m lines: two integers u v (edge between u and v).(u, v) exists → (v, u) also exists.m just increases.n × m where adj[i][j] = 1 if edge (i, j) exists, else 0.(u, v) and (v, u).O(n²) (costly for large sparse graphs).int main() {
int n, m;
cin >> n >> m;
// adjacency matrix for undirected graph
int adj[n+1][n+1]; // nodes are 1-based indexed
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
adj[u][v] = 1;
adj[v][u] = 1 // this statement will be removed in case of directed graph
}
return 0;
}
u→v and v→u) with space complexity:- O(2E) (E: number of edges)u→v) with space complexity:- O(E)int main() {
int n, m;
cin >> n >> m;
// adjacency list for undirected graph
vector<int> adj[n+1];
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u); // this statement will be removed in case of directed graph
}
return 0;
}
wt instead of 1.
int u, v, wt;
cin >> u >> v >> wt;
adj[u][v] = wt;
adj[v][u] = wt;
(neighbor, weight).
vector<pair<int,int>> adjList[n+1];
O(V+E)class Solution {
public:
vector<int> bfs(vector<vector<int>> &adj) {
int V = adj.size();
vector<int> visited(V, 0); // 0-indexed nodes
visited[0] = 1;
queue<int> q;
q.push(0); // starting from 0;
vector<int> ans;
while (!q.empty()){
int node = q.front();
q.pop();
ans.push_back(node);
// for neighbours
for (int it : adj[node]){
if (!visited[it]){
visited[it] = 1;
q.push(it);
}
}
}
return ans;
}
};
O(V+E)class Solution {
public:
void dfs(int node, vector<vector<int>>& adj, vector<int>& visited, vector<int> &ans) {
visited[node] = 1;
ans.push_back(node);
// traverse neighbours
for (auto it : adj[node]){
if (!visited[it]){
dfs(it, adj, visited, ans);
}
}
}
vector<int> dfsTraversal(vector<vector<int>>& adj) {
int v = adj.size();
vector<int> visited(v, 0);
vector<int> ans;
dfs(0, adj, visited, ans); // starting from 0
return ans;
}
};
class Solution {
public:
void dfs(int node, vector<int> adj[], vector<int>& visited, vector<int>& piece) {
visited[node] = 1;
piece.push_back(node);
// neighbours
for (auto nei : adj[node]) {
if (!visited[nei]) {
dfs(nei, adj, visited, piece);
}
}
}
vector<vector<int>> getComponents(int V, vector<vector<int>>& edges) {
int m = edges.size();
vector<int> adj[V];
for (int i = 0; i < m; i++){
adj[edges[i][0]].push_back(edges[i][1]);
adj[edges[i][1]].push_back(edges[i][0]);
}
vector<vector<int>> comp;
vector<int> visited(V, 0);
for (int i = 0; i < V; i++){
if (!visited[i]){
vector<int> piece;
dfs(i, adj, visited, piece);
comp.push_back(piece);
}
}
return comp;
}
};
class Solution {
public:
void dfs(int node, vector<vector<int>>& adjLs, vector<int>& vis) {
vis[node] = 1;
for(auto it: adjLs[node]) {
if(!vis[it]) {
dfs(it, adjLs, vis);
}
}
}
int numProvinces(vector<vector<int>> adj, int V) {
vector<vector<int>> adjLs(V);
// to change adjacency matrix to list as given 0/1 matrix
for(int i = 0; i < V; i++) {
for(int j = 0; j < V; j++) {
// self nodes are not considered
if(adj[i][j] == 1 && i != j) {
adjLs[i].push_back(j);
adjLs[j].push_back(i);
}
}
}
vector<int> vis(V, 0);
int cnt = 0;
for(int i = 0; i < V; i++) {
// if the node is not visited
if(!vis[i]) {
// counter to count the number of provinces
cnt++;
dfs(i, adjLs, vis);
}
}
return cnt;
}
};
class Solution {
public:
void dfs(int i, int j, vector<vector<char>>& grid, vector<vector<int>>& vis){
vis[i][j] = 1;
int n = grid.size();
int m = grid[0].size();
for (int di = -1 ; di <= 1; di++){
for (int dj = -1; dj <= 1; dj++){
int r = i+di;
int c = j+dj;
if (r >= 0 && r < n && c >= 0 && c < m && !vis[r][c] && grid[r][c] == 'L'){
vis[r][c] = 1;
dfs(r, c, grid, vis);
}
}
}
}
int countIslands(vector<vector<char>>& grid) {
int n = grid.size();
int m = grid[0].size();
vector<vector<int>> vis(n, vector<int>(m, 0));
int count = 0;
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
if (grid[i][j] == 'L' && !vis[i][j]){
count++;
dfs(i, j, grid, vis);
}
}
}
return count;
}
};
class Solution {
public:
void dfs(int i, int j, int color, vector<vector<int>>& image, vector<vector<int>>& ans, int original){
ans[i][j] = color;
int n = image.size();
int m = image[0].size();
vector<pair<int, int>> comb = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
for (int k = 0; k < 4; k++){
int r = i + comb[k].first;
int c = j + comb[k].second;
if (r >= 0 && r < n && c >= 0 && c < m && image[r][c] == original && ans[r][c] != color) {
dfs(r, c, color, image, ans, original);
}
}
}
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
int original = image[sr][sc];
vector<vector<int>> ans = image;
dfs(sr, sc, color, image, ans, original);
return ans;
}
};
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int n = grid.size();
int m = grid[0].size();
int total = 0;
int rotten = 0;
int mins = 0;
queue<pair<pair<int, int>, int>> q; // {{i, j}, time it get rotten}
vector<vector<int>> vis(n, vector<int>(m, 0));
for (int i = 0; i < n ; i++){
for (int j = 0; j < m; j++) {
if (grid[i][j] != 0) total++;
if (grid[i][j] == 2){
rotten++;
vis[i][j] = 2;
q.push({{i, j}, 0});
}
}
}
vector<pair<int, int>> comb = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
while (!q.empty()) {
int r = q.front().first.first;
int c = q.front().first.second;
int t = q.front().second;
q.pop();
mins = max(mins, t);
for (int i = 0; i < 4; i++){
int nr = r + comb[i].first;
int nc = c + comb[i].second;
if (nr >= 0 && nr < n && nc >= 0 && nc < m && grid[nr][nc] == 1 && vis[nr][nc] != 2){
vis[nr][nc] = 2;
rotten++;
q.push({{nr, nc}, t+1});
}
}
}
return total == rotten ? mins : -1;
}
};
class Solution {
public:
bool dfs(int node, int parent, vector<int> adj[], vector<int>& vis) {
vis[node] = 1;
for (auto it : adj[node]) {
if (!vis[it]) {
if (dfs(it, node, adj, vis)) return true;
}
else if (it != parent) return true; // visited node but not a parent node, then cycle
}
return false;
}
bool isCycle(int V, vector<vector<int>>& edges) {
int n = edges.size();
vector<int> adj[V];
for (int i = 0; i < n; i++){
adj[edges[i][0]].push_back(edges[i][1]);
adj[edges[i][1]].push_back(edges[i][0]);
}
vector<int> vis(V, 0);
for (int i = 0; i < V; i++){
if (!vis[i]){
if (dfs(i, -1, adj, vis)) return true;
}
}
return false;
}
};
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
int n = mat.size();
int m = mat[0].size();
vector<vector<int>> ans(n, vector<int>(m, 0));
vector<vector<int>> vis(n, vector<int>(m, 0));
queue<pair<pair<int,int>, int>> q; // <coordinates, steps>
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
if (mat[i][j] == 1){
vis[i][j] = 1;
q.push({{i, j}, 0});
}
}
}
vector<vector<int>> dirs = {{0,1}, {0,-1}, {1,0}, {-1,0}};
while (!q.empty()){
int r = q.front().first.first;
int c = q.front().first.second;
int steps = q.front().second;
q.pop();
ans[r][c] = steps;
for (auto d : dirs){
int nr = r + d[0];
int nc = c + d[1];
if (nr >= 0 && nr < n && nc >= 0 && nc < m && !vis[nr][nc]){
vis[nr][nc] = 1;
q.push({{nr, nc}, steps+1});
}
}
}
return ans;
}
};
class Solution {
public:
void dfs(int i, int j, vector<vector<int>>& vis, vector<vector<char>>& board){
vis[i][j] = 1;
int n = board.size();
int m = board[0].size();
vector<int> dr = {0, 0, 1, -1};
vector<int> dc = {1, -1, 0, 0};
for (int k = 0; k < 4; k++){
int nrow = i + dr[k];
int ncol = j + dc[k];
if (nrow > 0 && nrow < n && ncol > 0 && ncol < m && !vis[nrow][ncol] && board[nrow][ncol] == 'O'){
dfs(nrow, ncol, vis, board);
}
}
}
void solve(vector<vector<char>>& board) {
int n = board.size();
int m = board[0].size();
vector<vector<int>> vis(n, vector<int>(m, 0));
for (int j = 0; j < m; j++) { // traverse first and last row
if (!vis[0][j] && board[0][j] == 'O') dfs(0, j, vis, board);
if (!vis[n - 1][j] && board[n - 1][j] == 'O') dfs(n - 1, j, vis, board);
}
for (int i = 0; i < n; i++) { // traverse first and last column
if (!vis[i][0] && board[i][0] == 'O') dfs(i, 0, vis, board);
if (!vis[i][m - 1] && board[i][m - 1] == 'O') dfs(i, m - 1, vis, board);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// convert enclosed 'O' to 'X'
if (!vis[i][j] && board[i][j] == 'O') board[i][j] = 'X';
}
}
}
};
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
queue<pair<string, int>> q;
q.push({beginWord, 1});
unordered_set<string> st(wordList.begin(), wordList.end());
st.erase(beginWord);
while (!q.empty()){
string word = q.front().first;
int steps = q.front().second;
q.pop();
if (word == endWord) return steps;
for (int i = 0; i < word.size(); i++){
char original = word[i];
for (char ch = 'a'; ch <= 'z'; ch++){
word[i] = ch;
if (st.find(word) != st.end()){
st.erase(word);
q.push({word, steps+1});
}
}
word[i] = original;
}
}
return 0;
}
};
class Solution {
public:
// 0/1 adjacent colouring
bool dfs(int node, int c, vector<int> &color, vector<vector<int>>& adj){
color[node] = c;
for (auto it : adj[node]){
if (color[it] == -1){ // -1 means uncoloured
if (dfs(it, !c, color, adj) == false) return false;
} else if (color[it] == c){ // if previously coloured and have the same colour
return false;
}
}
return true;
}
bool isBipartite(vector<vector<int>>& adj)
int v = adj.size();
vector<int> color(v, -1);
for (int i = 0; i < v; i++){
if (color[i] == -1) {
if (dfs(i, 0, color, adj) == false) return false; // started with 0 colour
}
}
return true;
};
class Solution {
public:
bool dfs(int node, vector<vector<int>> &adj, vector<int> &vis, vector<int> &pathVis) {
vis[node] = 1;
pathVis[node] = 1;
for (auto it : adj[node]) {
// when the node is not visited
if (!vis[it]) {
if (dfs(it, adj, vis, pathVis) == true) return true;
}
// if the node has been previously visited but it has to be visited on the same path
else if (pathVis[it]) {
return true;
}
}
pathVis[node] = 0;
return false;
}
bool isCyclic(int V, vector<vector<int>> adj) {
vector<int> vis(V, 0);
vector<int> pathVis(V, 0);
for (int i = 0; i < V; i++) {
if (!vis[i]) {
if (dfs(i, adj, vis, pathVis) == true) return true;
}
}
return false;
}
};
class Solution {
public:
void dfs(int node, vector<vector<int>> &adj, vector<int> &vis, stack<int> &st) {
vis[node] = 1;
for (auto it : adj[node]) {
if (!vis[it]) {
dfs(it, adj, vis, st);
}
}
// After visiting all neighbors, push this node into the stack
st.push(node);
}
vector<int> topoSort(int V, vector<vector<int>> &adj) {
vector<int> vis(V, 0);
stack<int> st;
for (int i = 0; i < V; i++) {
if (!vis[i]) {
dfs(i, adj, vis, st);
}
}
vector<int> ans;
while (!st.empty()) {
ans.push_back(st.top());
st.pop();
}
return ans;
}
};
class Solution {
public:
vector<int> topoSort(int V, vector<vector<int>> &adj) {
vector<int> inDegree(V, 0); // To store the In-degrees of nodes
for (int i=0; i<V; i++) {
for (auto it : adj[i]) inDegree[it]++;
}
queue<int> q;
// Add nodes with no in-degree to queue
for (int i=0; i<V; i++) {
if(inDegree[i] == 0) q.push(i);
}
vector<int> ans;
while (!q.empty()) {
int node = q.front();
q.pop();
ans.push_back(node);
for(auto it : adj[node]) {
inDegree[it]--; // Decrement the in-degree of the neighbor
// Add the node to queue if its in-degree becomes zero
if(inDegree[it] == 0) q.push(it);
}
}
return ans;
}
};
class Solution {
public:
bool isCyclic(int V, vector<vector<int>>& adj) {
vector<int> indegree(V, 0);
for (int i = 0; i < V; i++) {
for (auto &it : adj[i]) {
indegree[it]++;
}
}
queue<int> q;
for (int i = 0; i < V; i++) {
if (indegree[i] == 0) q.push(i);
}
int count = 0; // Counter to track number of processed nodes
while (!q.empty()) {
int node = q.front();
q.pop();
count++; // Increment processed node count
for (auto &it : adj[node]) {
indegree[it]--;
if (indegree[it] == 0) q.push(it);
}
}
// If processed node count is not equal to total nodes, a cycle exists
return count != V;
}
};
class Solution {
public:
vector<int> eventualSafeNodes(int V, vector<vector<int>> adj) {
vector<vector<int>> adjRev(V); // Reverse adjacency list
vector<int> indegree(V, 0);
// Build the reverse graph and calculate indegrees
for (int i = 0; i < V; i++) {
for (auto it : adj[i]) {
adjRev[it].push_back(i); // Reverse the direction of edges
indegree[i]++;
}
}
queue<int> q; // Queue to store nodes with no outgoing edges (safe nodes)
vector<int> safe;
// Add all nodes with 0 indegree to the queue
for (int i = 0; i < V; i++) {
if (indegree[i] == 0) {
q.push(i);
}
}
while (!q.empty()) {
int node = q.front();
q.pop();
safe.push_back(node); // This node is safe
for (auto it : adjRev[node]) {
indegree[it]--;
if (indegree[it] == 0) {
q.push(it); // If indegree becomes 0, it is a safe node
}
}
}
sort(safe.begin(), safe.end());
return safe;
}
};
class Solution {
public:
// Function to find the shortest path from source to all vertices
vector<int> shortestPath(int V, vector<vector<int>> &edges, int src) {
vector<vector<int>> adj(V);
for (auto edge : edges){
adj[edge[0]].push_back(edge[1]);
adj[edge[1]].push_back(edge[0]);
}
// Initialize the distance array with a large value (treated as infinity)
vector<int> dist(V, 1e9);
dist[src] = 0;
queue<int> q;
q.push(src);
while (!q.empty()){
int node = q.front();
q.pop();
for (auto it : adj[node]){
// If a shorter path to neighbor is found
if (dist[node] + 1 < dist[it]){
dist[it] = dist[node] + 1;
q.push(it);
}
}
}
vector<int> ans(V, -1);
for (int i = 0; i < V; i++){
if (dist[i] != 1e9) ans[i] = dist[i];
}
return ans;
}
};
class Solution {
public:
vector<int> shortestPath(int V, int E, vector<vector<int>>& edges) {
vector<vector<int>> adj[V];
for(auto e : edges) {
adj[e[0]].push_back({e[1], e[2]});
}
vector<int> dist(V, 1e9);
dist[0] = 0;
queue<int> q;
q.push(0);
while(q.size()){
int node = q.front();
q.pop();
for(auto it : adj[node]){
int nbr = it[0] ;
int d = it[1];
if(dist[node] + d < dist[nbr]){
dist[nbr] = dist[node] + d;
q.push(nbr);
}
}
}
for(auto &i : dist) if (i==1e9) i = -1;
return dist ;
}
};
Efficient Approach using Topo Sort with DFS
class Solution {
public:
void topoSort(int node, vector<vector<int>> adj[], vector<int> &vis, stack<int> &st) {
vis[node] = 1;
for (auto it : adj[node]) {
int v = it.first;
if (!vis[v]) {
topoSort(v, adj, vis, st);
}
}
st.push(node);
}
vector<int> shortestPath(int V, int E, vector<vector<int>> &edges) {
vector<vector<int>> adj[V];
for (int i = 0; i < E; i++) {
int u = edges[i][0];
int v = edges[i][1];
int wt = edges[i][2];
adj[u].push_back({v, wt});// Store (target node, weight)
}
vector<int> vis(V);
stack<int> st;
for (int i = 0; i < N; i++) {
if (!vis[i]) {
topoSort(i, adj, vis, st);
}
}
vector<int> dist(V, 1e9);
dist[0] = 0;
// Process all nodes in topological order
while (!st.empty()) {
int node = st.top();
st.pop();
// Relax all outgoing edges from the current node
for (auto it : adj[node]) {
int v = it.first;
int wt = it.second;
if (dist[node] + wt < dist[v]) {
dist[v] = wt + dist[node];
}
}
}
for (int i = 0; i < V; i++) {
if (dist[i] == 1e9) dist[i] = -1;
}
return dist;
}
};
Using Min heap Priority Queue
- The algorithm starts with the source node at distance 0. At each step, the priority queue pops the node with the smallest distance. For every neighbor of that node, if the new distance through this node is shorter than the current stored distance, we update it and push the neighbor into the priority queue. This process continues until all nodes are processed.
// TC:- E.log(V)
vector<int> dijkstra(int V, vector<vector<pair<int,int>>> &adj, int src) {
vector<int> dist(V, 1e9);
dist[src] = 0;
// Min-heap storing {distance, node}
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> pq;
pq.push({0, src});// Push source into heap
while (!pq.empty()) {
int d = pq.top().first;
int node = pq.top().second;
pq.pop();
for (auto it : adj[node]) {
int next = it.first;
int wt = it.second;
if (dist[node] + wt < dist[next]) { // Relaxation check
dist[next] = dist[node] + wt;
pq.push({dist[next], next});
}
}
}
return dist;
}
Using Sets
- A set is preferred over a priority queue because it allows easier replacement of older pairs with worse distances.
- Since sets store elements in sorted(ascending) order, the node with the smallest distance is always processed first.
// TC:- E.log(V)
vector<int> dijkstra(int V, vector<vector<int>> adj[], int src) {
// Create a set ds for storing the nodes as a pair {dist, node}
set<pair<int, int>> s;
vector<int> dist(V, 1e9);
dist[s// where dist is the distance from source to the node.
// set stores the nodes in ascending order of the distances.rc] = 0;
s.insert({0, src}); // Insert the source node with a distance of 0.
while(!s.empty()) {
auto it = *(s.begin());
int node = it.second;
int d = it.first;
s.erase(it);
for(auto it : adj[node]) {
int nbr = it[0];
int w = it[1];
// If the new distance is smaller, update it
if(d + w < dist[nbr]) {
// Erase the previous entry of the adjacent node if it was visited previously with a larger cost.
if(dist[nbr] != 1e9) st.erase({dist[nbr], nbr});
dist[adjNode] = d + w;
st.insert({dist[adjNode], adjNode});
}
}
}
return dist;
}
Problem Statement: You are given a weighted undirected graph having n vertices numbered from 1 to n and m edges describing there are edges between a to b with some weight, find the shortest path between the vertex 1 and the vertex n, and if the path does not exist then return a list consisting of only -1.
For path, We use here a Parent Array which stores the parent of each node, representing where the node came from while traversing the graph using Dijkstra’s Algorithm
class Solution {
public:
vector<int> shortestPath(int n, int m, vector<vector<int>>& edges) {
vector<vector<pair<int, int>>> adj(n+1);
for (auto e : edges){
adj[e[0]].push_back({e[1], e[2]});
adj[e[1]].push_back({e[0], e[2]});
}
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // {distance, node}
pq.push({0, 1});
// Distance array to store shortest distances from the source node and parent array to track the path
vector<int> dist(n+1, 1e9), parent(n+1);
dist[1] = 0;
for (int i = 1; i <= n ; i++) parent[i] = i; // Initialize the parent array to track the path from the source
while (!pq.empty()){
auto i = pq.top();
pq.pop();
int node = i.second;
int d = i.first;
for (auto it : adj[node]){
int nbr = it.first;
int w = it.second;
if (d + w < dist[nbr]) {
dist[nbr] = d + w;
pq.push({dist[nbr], nbr});
// Update the parent of adjNode to the current node
parent[nbr] = node;
}
}
}
if (dist[n] == 1e9) return {-1};
// Store the final path in the 'path' array by backtracking from the destination node
vector<int> path;
int node = n;
while (parent[node] != node){
path.push_back(node);
node = parent[node]; // Move to the parent node
}
path.push_back(1); // Add the source node to the path
// Reverse the path to get the correct order from source to destination
reverse(path.begin(), path.end());
return path;
}
};
Problem Statement: Given an n * m matrix grid where each element can either be 0 or 1. You need to find the shortest distance between a given source cell to a destination cell. The path can only be created out of a cell if its value is 1. If the path is not possible between the source cell and the destination cell, then return -1.
we can directly use queue instead of Priority queue as there is unit distance from cell to cell.
class Solution {
public:
int shortestPath(vector<vector<int>> &grid, pair<int, int> source, pair<int, int> destination) {
// Edge Case: if the source is the same as the destination
if (source.first == destination.first && source.second == destination.second) return 0;
int n = grid.size();
int m = grid[0].size();
vector<vector<int>> dist(n, vector<int>(m, 1e9));
dist[source.first][source.second] = 0;
queue<pair<int, pair<int, int>>> q;
// Push the source cell into the queue with distance 0
q.push({0, {source.first, source.second}});
// Define the possible directions (up, right, down, left)
int dr[] = {-1, 0, 1, 0};
int dc[] = {0, 1, 0, -1};
while (!q.empty()) {
auto it = q.front();
q.pop();
int d = it.first;
int r = it.second.first;
int c = it.second.second;
for (int i = 0; i < 4; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
if (nr >= 0 && nr < n && nc >= 0 && nc < m && grid[nr][nc] == 1 && d + 1 < dist[nr][nc]) {
dist[nr][nc] = d + 1;
// If destination is reached, return the distance
if (nr == destination.first && nc == destination.second) return d + 1;
q.push({d+1, {nr, nc}});
}
}
}
// If no path is found from source to destination
return -1;
}
};
Problem Statement: There are n cities and m edges connected by some number of flights. You are given an array of flights where flights[i] = [ fromi, toi, pricei] indicates that there is a flight from city fromi to city toi with cost price. You have also given three integers src, dst, and k, and return the cheapest price from src to dst with at most k stops. If there is no such route, return -1.
Our priority first will be stops and since stops increase one by one, no need of priority queue, we can use simple queue with first entry of stops in dijkstra.
class Solution {
public:
int CheapestFLight(int n, vector<vector<int>> &flights, int src, int dst, int K) {
vector<vector<pair<int, int>>> adj(n);
for (auto it : flights) {
adj[it[0]].push_back({it[1], it[2]});
}
// queue that will store pairs of {stops, {node, dist}}
queue<pair<int, pair<int, int>>> q;
q.push({0, {src, 0}});
vector<int> dist(n, 1e9);
dist[src] = 0;
while (!q.empty()) {
auto i = q.front();
q.pop();
int stops = i.first; // Number of stops so far
int node = i.second.first; // Current node
int cost = i.second.second; // Cost to reach the current node
// If the number of stops exceeds K, continue to the next iteration
if (stops > K) continue;
for (auto it : adj[node]) {
int nbr = it.first;
int w = it.second;
if (cost + w < dist[abr] && stops <= K) {
dist[adjNode] = cost + w;
q.push({stops + 1, {nbr, cost + w}});
}
}
}
if (dist[dst] == 1e9) return -1; // If destination node is unreachable, return -1
return dist[dst];
}
};
class Solution {
public:
int CheapestFlight(int n, vector<vector<int>> &flights) {
vector<vector<pair<int, int>>> adj(n);
for (auto it : flights) {
adj[it[0]].push_back({it[1], it[2]});
adj[it[1]].push_back({it[0], it[2]});
}
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
pq.push({0, 0});
// Initialize the distance array and ways array
vector<int> dist(n, INT_MAX), ways(n, 0);
dist[0] = 0;
ways[0] = 1; // There's 1 way to reach the source (itself)
int mod = (int)(1e9 + 7);
while (!pq.empty()) {
int d = pq.top().first;
int node = pq.top().second;
pq.pop();
for (auto it : adj[node]) {
int nbr = it.first;
int w = it.second;
if (d + w < dist[nbr]) {
dist[nbr] = d + w;
pq.push({d + w, nbr});
ways[nbr] = ways[node];// Copy the number of ways to the new node
}
// If the same shortest path is found, update the number of ways
else if (d + w == dist[nbr]) {
// Increment the number of ways
ways[nbr] = (ways[nbr] + ways[node]) % mod;
}
}
}
return ways[n-1] % mod;
}
};
Problem Statement: Given start, end, and an array arr of n numbers. At each step, the start is multiplied by any number in the array and then a mod operation with 100000 is done to get the new start. we have to find the minimum steps in which the end can be achieved starting from the start. If it is not possible to reach the end, then return -1.
we define a Queue which would contain pairs of the type {num, steps}, where ‘steps’ indicates the currently updated value of no. of steps taken to reach from source to the current ‘num’.
class Solution {
public:
int minimumMultiplications(vector<int>& arr, int start, int end) {
if (start == end) return 0;
vector<int> dist(100000, 1e9);
dist[start] = 0;
int mod = 100000;
queue<pair<int, int>> q;
q.push({start, 0});
while (!q.empty()){
auto i = q.front();
q.pop();
int node = i.first;
int steps = i.second;
for (auto it : arr){
int num = (node * it) % mod;
if (steps + 1 < dist[num]){
dist[num] = steps + 1;
if (num == end) return steps + 1;
q.push({num, steps+1});
}
}
}
return -1;
}
};
Why Bellman-Ford? It works with negative edges and can detect negative cycles. For undirected graphs, treat each edge as two directed edges, since bellman-ford works only for directed graph.
class Solution {
public:
vector<int> Bellman_ford(int V, vector<vector<int>>& edges, int src) {
vector<int> dist(V, 1e8);
dist[src] = 0;
// Relaxation of all the edges (V-1) times
for (int i = 0; i < V-1; i++) {
for (auto it : edges) {
int u = it[0];
int v = it[1];
int w = it[2];
if (dist[u] != 1e8 && dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
}
}
}
// Vth relaxation to check negative cycle
for (auto it : edges) {
int u = it[0];
int v = it[1];
int w = it[2];
if (dist[u] != 1e8 && dist[u] + w < dist[v]) {
return {-1};
}
}
return dist;
}
};
This algorithm works for both the directed and undirected weighted graphs and can handle graphs with both positive and negative weight edges. It does not work for the graphs with negative cycles.
TC :- O(V3)
class Solution {
public:
void floydWarshall(vector<vector<int>> &dist) {
int V = dist.size();
// Try every vertex 'k' as an intermediate node between (i → j)
for (int k = 0; k < V; k++) {
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if(dist[i][k]==1e8 || dist[k][j]==1e8) continue;
dist[i][j] = min(dist[i][j],dist[i][k] + dist[k][j]);
}
}
}
}
};
for (int i = 0; i < V; i++)
if (dist[i][i] < 0) return cycle exists;
class Solution {
public:
int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) {
vector<vector<int>> dist(n, vector<int>(n, INT_MAX));
for (auto it : edges){
dist[it[0]][it[1]] = it[2];
dist[it[1]][it[0]] = it[2];
}
for (int i = 0; i < n ; i++) dist[i][i] = 0;
// Floyd-Warshall Algorithm to find the shortest paths between all pairs of cities
for (int k = 0 ; k < n; k++){
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (dist[i][k] == INT_MAX || dist[k][j] == INT_MAX) continue;
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
int city_count = n;
int cityno = -1;
// Check each city and count the number of cities within the threshold distance
for (int c = 0; c < n; c++){
int cnt = 0;
for (int adjcity = 0; adjcity < n; adjcity++){
if (dist[c][adjcity] <= distanceThreshold) cnt++;
}
// Update the city with the least number of reachable cities
if (cnt <= city_count){
city_count = cnt;
cityno = c;
}
}
return cityno;
}
};
Spanning Tree :- A spanning tree is a tree-like subgraph of a connected undirected graph in which we have N nodes(i.e. All the nodes present in the original graph) and N-1 edges and all nodes are reachable from each other.
There may exist multiple minimum spanning trees for a graph like a graph may have multiple spanning trees.
The task is to find the sum of weights of the edges of the MST or the MST array if we want
We will be requiring an visited array and a min-heap priority queue. We need another array(MST) as well if we wish to store the edge information of the minimum spanning tree in the form {u, v}(u = starting node, v = ending node)
The intuition of this algorithm is the greedy technique used for every node. If we carefully observe, for every node, we are greedily selecting its unvisited adjacent node with the minimum edge weight(as the priority queue here is a min-heap and the topmost element is the node with the minimum edge weight). Doing so for every node, we can get the sum of all the edge weights of the minimum spanning tree and the spanning tree itself(if we wish to) as well
class Solution {
public:
int spanningTree(int V, vector<vector<int>>& edges) {
vector<vector<pair<int, int>>> adj(V);
for (auto it : edges){
adj[it[0]].push_back({it[1], it[2]});
adj[it[1]].push_back({it[0], it[2]});
}
priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq;
pq.push({0, 0, -1}); // {edge_weight, node, parent_node}
vector<int> vis(V, 0);
int sum = 0; // Total weight of MST
vector<pair<int, int>> mst; // Stores MST edges as {parent, node}
while (!pq.empty()) {
auto it = pq.top();
pq.pop();
int wt = it[0];
int node = it[1];
int parent = it[2];
if (vis[node] == 1) continue; // If node already visited, skip
// Mark as visited and add edge wt to mst
vis[node] = 1;
sum += wt;
if (parent != -1) mst.push_back({parent, node});
for (auto i : adj[node]) {
int adjNode = i.first;
int adjWt = i.second;
if (!vis[adjNode]) {
pq.push({adjWt, adjNode, node});
}
}
}
/* At this point:
- "sum" contains total weight of MST
- "mst" contains all MST edges as (u, v) */
return sum;
}
};
findUParent(): Finds the “Ultimate Parent” of a particular node.Union(): Connects two nodes (or their components) into a single component, basically adds an edge between two nodes. Uses two strategies : Union by rank, Union by size.O(log N) for each case , so during the findUParent() recursion, we connect every visited node directly to the Ultimate Parent which flattens the tree structure, reducing future lookup time to nearly O(1).O(4α), where α (Alpha) is the Inverse Ackermann function which is very small and close to 1 , which is approximately considered Constant Time O(1).#include <bits/stdc++.h>
using namespace std;
class DisjointSet {
vector<int> rank, parent, size;
public:
// Constructor
DisjointSet(int n) {
rank.resize(n + 1, 0); // For Union by Rank (1 based indexing)
size.resize(n + 1, 1); // For Union by Size
parent.resize(n + 1);
for (int i = 0; i <= n; i++) {
parent[i] = i; // Initially, every node is its own parent
}
}
// Find Ultimate Parent with Path Compression
int findUPar(int node) {
if (node == parent[node]) return node;
return parent[node] = findUPar(parent[node]);
}
// Union by Rank
void unionByRank(int u, int v) {
int ulp_u = findUPar(u);
int ulp_v = findUPar(v);
if (ulp_u == ulp_v) return; // Already in same component
if (rank[ulp_u] < rank[ulp_v]) {
parent[ulp_u] = ulp_v;
} else if (rank[ulp_v] < rank[ulp_u]) {
parent[ulp_v] = ulp_u;
} else {
parent[ulp_v] = ulp_u;
rank[ulp_u]++;
}
}
// Union by Size (Preferred)
void unionBySize(int u, int v) {
int ulp_u = findUPar(u);
int ulp_v = findUPar(v);
if (ulp_u == ulp_v) return;
if (size[ulp_u] < size[ulp_v]) {
parent[ulp_u] = ulp_v;
size[ulp_v] += size[ulp_u];
} else {
parent[ulp_v] = ulp_u;
size[ulp_u] += size[ulp_v];
}
}
};
int main() {
DisjointSet ds(7);
ds.unionBySize(1, 2);
ds.unionBySize(2, 3);
ds.unionBySize(4, 5);
ds.unionBySize(6, 7);
ds.unionBySize(5, 6);
// Check if 3 and 7 are connected
if (ds.findUPar(3) == ds.findUPar(7)) {
cout << "Same\n";
} else cout << "Not same\n";
ds.unionBySize(3, 7);
if (ds.findUPar(3) == ds.findUPar(7)) {
cout << "Same\n";
} else cout << "Not same\n";
return 0;
}
// Output:-
// Not same
// Same
class DisjointSet {
vector<int> parent, size;
public:
DisjointSet(int n) {
size.resize(n + 1, 1);
parent.resize(n + 1);
for (int i = 0; i <= n; i++) {
parent[i] = i;
}
}
// Function to find ultimate parent
int findUPar(int node) {
if (node == parent[node])
return node;
return parent[node] = findUPar(parent[node]);
}
// Function to implement union by size
void unionBySize(int u, int v) {
int ulp_u = findUPar(u);
int ulp_v = findUPar(v);
if (ulp_u == ulp_v) return;
if (size[ulp_u] < size[ulp_v]) {
parent[ulp_u] = ulp_v;
size[ulp_v] += size[ulp_u];
}
else {
parent[ulp_v] = ulp_u;
size[ulp_u] += size[ulp_v];
}
}
};
class Solution{
public:
int spanningTree(int V, vector<vector<int>> adj[]) {
vector<pair<int, pair<int, int>>> edges; // To store the edges
for (int i = 0; i < V; i++) {
for (auto it : adj[i]) {
int v = it[0]; // Node v
int wt = it[1]; // edge weight
int u = i; // Node u
edges.push_back({wt, {u, v}});
}
}
DisjointSet ds(V); // Creating a disjoint set of V vertices
sort(edges.begin(), edges.end()); // Sorting the edges based on their weights
int sum = 0;
for (auto it : edges) {
int wt = it.first;
int u = it.second.first;
int v = it.second.second;
// Join the nodes if not in the same set
if (ds.findUPar(u) != ds.findUPar(v)) {
sum += wt;// Update the sum of edges in MST
ds.unionBySize(u, v); // Union the nodes
}
}
return sum;
}
};
class DisjointSet {
vector<int> parent, size;
public:
DisjointSet(int n) {
size.resize(n + 1, 1);
parent.resize(n + 1);
for (int i = 0; i <= n; i++) {
parent[i] = i;
}
}
int findUPar(int node) {
if (node == parent[node])
return node;
return parent[node] = findUPar(parent[node]);
}
void unionBySize(int u, int v) {
int ulp_u = findUPar(u);
int ulp_v = findUPar(v);
if (ulp_u == ulp_v) return;
if (size[ulp_u] < size[ulp_v]) {
parent[ulp_u] = ulp_v;
size[ulp_v] += size[ulp_u];
}
else {
parent[ulp_v] = ulp_u;
size[ulp_u] += size[ulp_v];
}
}
};
class Solution {
public:
vector<vector<string>> accountsMerge(vector<vector<string>> &details) {
int n = details.size();
DisjointSet ds(n);
unordered_map<string, int> mpp; // Map to store email -> account index
// Union accounts having common emails
for (int i = 0; i < n; i++) {
for (int j = 1; j < details[i].size(); j++) {
string mail = details[i][j];
if (mpp.find(mail) == mpp.end()) {
mpp[mail] = i;
} else {
ds.unionBySize(i, mpp[mail]);
}
}
}
// Group emails under ultimate parent
vector<string> mergedMail[n];
for (auto it : mpp) {
string mail = it.first;
int node = ds.findUPar(it.second);
mergedMail[node].push_back(mail);
}
// Prepare final merged result
vector<vector<string>> ans;
for (int i = 0; i < n; i++) {
if (mergedMail[i].empty()) continue;
sort(mergedMail[i].begin(), mergedMail[i].end());
vector<string> temp;
temp.push_back(details[i][0]);
for (auto &mail : mergedMail[i]) {
temp.push_back(mail);
}
ans.push_back(temp);
}
return ans;
}
};
You are given a n,m which means the row and column of the 2D matrix and an array A of size k denoting the number of operations. Matrix elements is 0 if there is water or 1 if there is land. Originally, the 2D matrix is all 0 which means there is no land in the matrix. Each operation has two integer A[i][0], A[i][1] means that you can change the cell matrix[A[i][0]][A[i][1]] from sea to island. Return how many island are there in the matrix after each operation.You need to return an array of size k.
class Solution {
public:
vector<int> numOfIslands(int n, int m, vector<vector<int>> &operators) {
vector<vector<int>> mat(n, vector<int>(m, 0));
DisjointSet ds(n*m);
vector<int> ans;
int count = 0;
for (auto it : operators){
int r = it[0];
int c = it[1];
if (mat[r][c] == 1){ // Skip if the cell is already land
ans.push_back(count);
continue;
}
mat[r][c] = 1;
count++;
vector<vector<int>> dirs = {{1,0}, {-1,0}, {0,1}, {0,-1}};
for (auto d : dirs){
int nr = r + d[0];
int nc = c + d[1];
if (nr >= 0 && nr < n && nc >= 0 && nc < m && mat[nr][nc] == 1){
int node = r*m +c;
int adjnode = nr*m + nc;
// creating islands
if (ds.findUPar(node) != ds.findUPar(adjnode)){
count--;
ds.unionBySize(node, adjnode);
}
}
}
ans.push_back(count);
}
return ans;
}
};
class Solution {
public:
void dfs(int node, vector<int> &vis, vector<vector<int>> &adj, stack<int> &st){
vis[node] = 1;
for (auto it : adj[node]){
if (!vis[it]) dfs(it, vis, adj, st);
}
st.push(node);
}
// DFS on transposed graph
void dfs2(int node, vector<int> &vis, vector<vector<int>> &adjT){
vis[node] = 1;
for (auto it : adjT[node]){
if (!vis[it]) dfs2(it, vis, adjT);
}
}
// Function to find number of strongly connected components
int kosaraju(vector<vector<int>> &adj) {
int V = adj.size();
vector<int> vis(V, 0);
stack<int> st;
// Do DFS to fill stack by finishing times
for (int i = 0; i < V; i++){
if (!vis[i]) dfs(i, vis, adj, st);
}
// Build the transpose(reverse edge) graph
vector<vector<int>> adjT(V);
for (int i = 0; i < V; i++){
vis[i] = 0; // reset visited
for (auto it : adj[i]){
adjT[it].push_back(i);
}
}
int scc = 0;
// Process stack to count SCCs
while (!st.empty()){
int node = st.top();
st.pop();
if (!vis[node]){
scc++;
dfs2(node, vis, adjT);
}
}
return scc;
}
};
vis[] – to check whether a node has already been visited in DFS.tin[] – stores the time of insertion (first visit) of each node during DFS.low[] – stores the min lowest time of insertion among all adjacent nodes apart from the parent, so that if edge from parent is removed can the particular node can be reached other than parent.// TC:- O(V+2E)
class Solution {
private:
int timer = 1; // Global timer to assign discovery times
void dfs(int node, int parent, vector<int> &vis, vector<int> adj[], int tin[], int low[], vector<vector<int>> &bridges) {
vis[node] = 1; // Mark current node as visited
tin[node] = low[node] = timer; // Set discovery time and low-link value
timer++;
for (auto it : adj[node]) { // Explore all adjacent nodes
if (it == parent) continue; // Skip the edge to parent
if (vis[it] == 0) {
dfs(it, node, vis, adj, tin, low, bridges);
// Update low-link value of current node
low[node] = min(low[node], low[it]);
// Check if the edge is a bridge
if (low[it] > tin[node]) {
bridges.push_back({it, node});
}
} else {
// Back edge: update low-link value
low[node] = min(low[node], low[it]);
}
}
}
public:
vector<vector<int>> criticalConnections(int n, vector<vector<int>>& connections) {
vector<int> adj[n];
for (auto it : connections) {
int u = it[0], v = it[1];
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<int> vis(n, 0);
int tin[n]; // Discovery time
int low[n]; // Lowest reachable time
vector<vector<int>> bridges;
dfs(0, -1, vis, adj, tin, low, bridges);
return bridges;
}
};
An articulation point is a vertex whose removal, along with all its connected edges, increases the number of connected components in the graph. The graph may contain more than one connected component.
class Solution {
private:
int timer = 1;
void dfs(int node, int parent, vector<int> &vis, int tin[], int low[], vector<int> &mark, vector<int> adj[]) {
vis[node] = 1;
tin[node] = low[node] = timer++; // set discovery and low time
int child = 0; // for special case of starting node.
for (auto it : adj[node]) {
if (it == parent) continue; // skip the edge to parent
if (!vis[it]) {
dfs(it, node, vis, tin, low, mark, adj);
low[node] = min(low[node], low[it]); // update low time
// Check articulation condition (excluding root)
if (low[it] >= tin[node] && parent != -1) {
mark[node] = 1;
}
child++;
} else {
// back edge case
low[node] = min(low[node], tin[it]);
}
}
// If root node has more than one child
if (parent == -1 && child > 1) {
mark[node] = 1;
}
}
public:
vector<int> articulationPoints(int n, vector<int> adj[]) {
vector<int> vis(n, 0);
vector<int> mark(n, 0); // mark to articulation points
int tin[n], low[n];
for (int i = 0; i < n; i++) {
if (!vis[i]) dfs(i, -1, vis, tin, low, mark, adj);
}
vector<int> ans;
for (int i = 0; i < n; i++) {
if (mark[i]) ans.push_back(i);
}
return ans.empty() ? vector<int>{-1} : ans;
}
};