Problem Statement
You are given an undirected graph represented as an adjacency matrix isConnected
where:
isConnected[i][j] = 1
means that thei
-th andj
-th nodes are directly connected.isConnected[i][j] = 0
means that thei
-th andj
-th nodes are not directly connected.
A province is a group of directly or indirectly connected cities, and no city outside the group is connected to any city in the group.
Your task is to find the total number of provinces (i.e., connected components in the graph).
Input:
- An integer matrix
isConnected
of sizen x n
wheren
is the number of cities.
Output:
- An integer representing the number of provinces.
Examples
Example 1:
Input: adj = [
[1, 1, 0],
[1, 1, 0],
[0, 0, 1]
]
Output: 2
Explanation: There are two provinces: one composed of cities 0 and 1, and one consisting of city 2.
Example 2:
Input: adj = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
]
Output: 3
Explanation: Each city is its own province as there are no connections between them.
Different Approaches
1️⃣ Depth-First Search (DFS)
We can treat this problem as finding the number of connected components in an undirected graph. Each connected component is considered a province.
- Traverse the graph using DFS starting from each unvisited city.
- Mark all cities connected to the current city as visited.
- Each time a new DFS traversal starts, it represents the discovery of a new province.
Steps:
- Initialize a visited array.
- For each city, if it has not been visited, start a DFS to mark all connected cities.
- Increment the province count when you start a new DFS.
Code:
#include <iostream>
#include <vector>
using namespace std;
void dfs(int city, vector<vector<int>>& isConnected, vector<bool>& visited) {
visited[city] = true;
for (int i = 0; i < isConnected.size(); i++) {
if (isConnected[city][i] == 1 && !visited[i]) {
dfs(i, isConnected, visited);
}
}
}
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size();
vector<bool> visited(n, false);
int provinces = 0;
for (int i = 0; i < n; i++) {
if (!visited[i]) {
dfs(i, isConnected, visited);
provinces++;
}
}
return provinces;
}
int main() {
vector<vector<int>> isConnected = {{1,1,0},{1,1,0},{0,0,1}};
cout << "Number of Provinces: " << findCircleNum(isConnected) << endl;
return 0;
}
Complexity Analysis:
- Time Complexity:
O(n^2)
wheren
is the number of cities, because in the worst case, we need to visit each city and check every connection between cities. - Space Complexity:
O(n)
for the visited array, plusO(n)
recursion stack space for DFS.
2️⃣ Breadth-First Search (BFS)
Instead of DFS, we can also use a queue to perform BFS for each unvisited city. BFS will traverse all cities connected to the starting city.
Code:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
void bfs(int city, vector<vector<int>>& isConnected, vector<bool>& visited) {
queue<int> q;
q.push(city);
visited[city] = true;
while (!q.empty()) {
int current = q.front();
q.pop();
for (int i = 0; i < isConnected.size(); i++) {
if (isConnected[current][i] == 1 && !visited[i]) {
q.push(i);
visited[i] = true;
}
}
}
}
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size();
vector<bool> visited(n, false);
int provinces = 0;
for (int i = 0; i < n; i++) {
if (!visited[i]) {
bfs(i, isConnected, visited);
provinces++;
}
}
return provinces;
}
int main() {
vector<vector<int>> isConnected = {{1,1,0},{1,1,0},{0,0,1}};
cout << "Number of Provinces: " << findCircleNum(isConnected) << endl;
return 0;
}
Complexity Analysis:
- Time Complexity:
O(n^2)
wheren
is the number of cities, as we still need to traverse each city and check all its connections. - Space Complexity:
O(n)
for the visited array, andO(n)
for the BFS queue.
3️⃣ By Converting Adjacency Matrix to Adjacency List
Approach
- Convert the given adjacency matrix to adjacency list for easy traversal.
- Initialize a visited array to mark the nodes that are visited and a counter to count the number of provinces found.
- Every time a new node is visited, a new province is founds so increment the counter and traverse all the nodes connected to current node.
- By the end of the exploration, the counter will get us the total number of provinces.
Dry Run:
Code:
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
// Function for BFS traversal
void bfs(int node, vector<int> adjLs[], int vis[]) {
// Mark the node as visited
vis[node] = 1;
// Queue required for BFS traversal
queue <int> q;
// To start traversal from node
q.push(node);
/* Keep on traversing till
the queue is not empty */
while(!q.empty()) {
// Get the node
int i = q.front();
q.pop();
// Traverse its unvisited neighbours
for(auto adjNodes: adjLs[i]) {
if(vis[adjNodes] != 1) {
// Mark the node as visited
vis[adjNodes] = 1;
// Add the node to queue
q.push(adjNodes);
}
}
}
}
// Function for DFS traversal
void dfs(int node, vector<int> adjLs[], int vis[]) {
// Mark the node as visited
vis[node] = 1;
// Traverse its unvisited neighbours
for(auto it: adjLs[node]) {
if(!vis[it]) {
// Recursively perform DFS
dfs(it, adjLs, vis);
}
}
}
public:
/* Function to find the number of
provinces in the given graph */
int numProvinces(vector<vector<int>> adj) {
// Variable to store number of nodes
int V = adj.size();
// To store adjacency list
vector<int> adjLs[V];
// Convert adjacency matrix to adjacency list
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);
}
}
}
// Visited array
int vis[V] = {0};
// Variable to store number of provinces
int cnt = 0;
// Start Traversal
for(int i=0; i < V; i++) {
// If the node is not visited
if(!vis[i]) {
// Increment counter
cnt++;
// Start traversal from current node
bfs(i, adjLs, vis);
//dfs(i, adjLs, vis);
}
}
// Return the count
return cnt;
}
};
int main() {
vector<vector<int>> adj =
{
{1, 0, 0, 1},
{0, 1, 1, 0},
{0, 1, 1, 0},
{1, 0, 0, 1}
};
/* Creating an instance of
Solution class */
Solution sol;
/* Function call to find the
provinces in the given graph */
int ans = sol.numProvinces(adj);
cout << "The number of provinces in the given graph is: " << ans;
return 0;
}
Complexity Analysis:
- Time Complexity:
O(V + E)
(where V denotes the number of nodes, E denotes the number of edges).- Converting adjacency matrix to list takes
O(V^2)
time (equivalent toO(V + E)
. - Considering overall, all the nodes are visited through traversal techniques which takes
O(V + E)
time.
- Converting adjacency matrix to list takes
- Space Complexity:
O(V + E)
- Storing the adjacency list takes
O(E)
space. - Any traversal technique takes
O(V)
extra space.
- Storing the adjacency list takes