This problem is counting connected components in an undirected graph. Each city is a node, and isConnected[i][j] = 1 means there is an edge between node i and node j. A "province" is another word for a connected component: a maximal set of nodes where you can reach any node from any other node through some path.
The input is an adjacency matrix, not an adjacency list. To find the neighbors of city i, scan row i and collect every column j where isConnected[i][j] = 1 and j != i (every city is connected to itself, which is not a useful edge).
The question reduces to: how many separate groups of cities exist? Start at any unvisited city and explore all cities reachable from it. That covers one complete province. The number of times you have to start a fresh exploration equals the number of provinces.
1 <= n <= 200 → The matrix has at most 40,000 entries. Any O(n^2) approach reading the full matrix is the natural fit, and there is no reason to push for anything faster.isConnected[i][j] == isConnected[j][i] → The graph is undirected, so an edge between i and j can be processed from either side.isConnected[i][i] == 1 → The diagonal is always set. These self-loops are skipped, since a city in its own province does not connect it to anything new.Start at a city you have not visited and explore as far as you can, visiting every city reachable from it. Once no new cities are reachable, one complete province has been found. Look for the next unvisited city and repeat. The number of fresh DFS traversals you start equals the number of provinces.
This is the connected components algorithm. The visited array prevents counting the same city twice: a DFS only starts from cities that no earlier traversal reached, so each province is counted exactly once.
visited boolean array of size n, initialized to false.provinces = 0.i from 0 to n - 1:i has not been visited, start a DFS from city i and increment provinces.i as visited, then recursively visits all unvisited neighbors of i.i, scan row i of the matrix: city j is a neighbor if isConnected[i][j] == 1 and j != i.provinces.DFS uses recursion, so a long chain of connected cities can drive the call stack to O(n) depth. The next approach explores the same components iteratively.
BFS explores the same components as DFS, but iteratively. Instead of recursing into one path, it uses a queue to expand outward from the starting city, marking neighbors as visited as they are enqueued. The component-counting logic is unchanged: each fresh BFS that starts from an unvisited city is one more province.
BFS uses an explicit queue instead of the call stack, which removes the O(n) recursion depth that DFS can hit on a long chain of connected cities.
BFS and DFS both reach exactly the set of nodes connected to the start node, only in a different order. Component counting depends only on which nodes get marked visited before a province is closed out, not on the order they are reached, so BFS and DFS always return the same count.
visited boolean array of size n, initialized to false.provinces = 0.i from 0 to n - 1:i has not been visited, start a BFS from city i and increment provinces.i to a queue, mark it visited. While the queue is not empty, dequeue a city, scan its row for unvisited neighbors, and enqueue them.provinces.DFS and BFS both traverse the graph to discover components. The next approach counts components without traversal, by merging connected cities into groups as it scans the edges.
Union Find (also called Disjoint Set Union) processes each edge and merges the two cities it connects into the same set. At the end, the number of distinct sets equals the number of provinces.
Start with each city in its own set, so the count of sets is n. For each connection isConnected[i][j] = 1, merge the sets containing city i and city j. A merge that joins two previously separate sets reduces the count by 1; if the two cities are already in the same set, nothing changes. Tracking that running count avoids a separate pass at the end: after all edges are processed, the count is the number of provinces, because two cities end up in the same set exactly when a path of edges connects them.
Path compression and union by rank are two standard optimizations that keep the find and union operations close to O(1) amortized.
parent array where parent[i] = i (each city is its own root).rank array of all zeros (used for union by rank).provinces = n (initially each city is its own province).(i, j) where i < j and isConnected[i][j] == 1:i and city j.provinces.provinces.