1267. Count Servers that Communicate
Problem Description
In this problem, we're given a representation of a server center as a m * n
integer matrix called grid
, where each cell in the grid can either contain a server (represented by the number 1
) or not contain a server (represented by the number 0
). The crucial point to understand is that servers are considered to be able to communicate with each other if and only if they are located in the same row or the same column. The objective is to calculate how many servers are able to communicate with at least one other server within the grid.
Flowchart Walkthrough
Let's analyze the problem using the Flowchart. Here's a step-by-step walkthrough for LeetCode 1267. Count Servers that Communicate:
-
Is it a graph?
- Yes: The grid of servers can be considered a graph where each server (represented by a value of 1) is a node, and there are implied connections along rows and columns if there are multiple servers.
-
Is it a tree?
- No: There isn't necessarily a hierarchical structuring among servers, and connections don't form parent-child relationships.
-
Is the problem related to directed acyclic graphs (DAG)?
- No: The problem is about connecting servers through rows and columns, which doesn't involve directed acyclic properties or topological sorting.
-
Is the problem related to shortest paths?
- No: The goal is to count servers that can communicate, not to find the shortest path between servers.
-
Does the problem involve connectivity?
- Yes: The problem requires counting the servers in connected components, where each connected component forms by servers communicating either in the same row or column.
-
Is the graph weighted?
- No: There are no weights involved in the server grid; it's simply about presence (1) or absence (0) of a server at any position.
Based on these answers, we conclude that the problem should be addressed by exploring connectivity in the grid where servers are nodes. Since the grid is unweighted and involves checking connected formations, an appropriate approach to solving this would be either Depth-First Search (DFS) or Breadth-First Search (BFS). Given we can efficiently handle DFS in this context, and since DFS can be utilized recursively to explore all connected servers once a communicating server is found, DFS would be an eligible method to apply. Thus, Depth-First Search pattern is a relevance in solving this problem.
Intuition
To determine the number of servers that can communicate with others, we need a way to check each server and see if there is at least one other server on the same row or column. The solution approach can start by keeping two separate arrays, row
and col
, to keep track of how many servers are on each row and column respectively.
We then iterate over the entire grid, counting how many servers are in each row and in each column. This is done by going through each cell of the matrix, and whenever we encounter a server (a cell with a value of 1
), we increment the corresponding row and column counters by 1.
Once we have the counts of servers in each row and column, we can again iterate over the grid. This time, for each server, we check whether the corresponding row or column has more than one server (which means the count is greater than 1
). If so, the server can communicate with others, and it should be included in the final count. We do this check using a generator expression and pass it to sum
function to get the total count of servers that can communicate.
This approach efficiently factors in both row-wise and column-wise communication, ensuring no double counting of servers.
Learn more about Depth-First Search, Breadth-First Search and Union Find patterns.
Solution Approach
The implementation of the solution uses a two-pass algorithm across the rows and columns of the server grid matrix. The algorithm selectively counts and then aggregates server connections based on the communication criteria. The solution leverages simple data structures – two lists named row
and col
– to store the counts of servers in each row and column, respectively.
Here's a step-by-step walk-through of the algorithm:
-
Initialize two lists,
row
andcol
, with a size equal to the number of rowsm
and columnsn
in input grid, respectively, and set all values to0
. These lists hold counts of servers in each row and column.row = [0] * m col = [0] * n
-
Iterate over each cell in the
grid
matrix to fill in therow
andcol
arrays. Whenever a server (1
in grid) is found, increment the respective row's and column's count.for i in range(m): for j in range(n): if grid[i][j]: row[i] += 1 col[j] += 1
-
After populating the
row
andcol
arrays, we carry out a second iteration over thegrid
. For each server found, check if it can communicate (i.e., ifrow[i]
orcol[j]
is greater than 1).The expression
grid[i][j] and (row[i] > 1 or col[j] > 1)
evaluates toTrue
if there is a server ingrid[i][j]
and it can communicate with at least one server in the same row or column.The generator expression inside the
sum
function goes through all cells in the grid, evaluates the above condition, and if it isTrue
, it contributes1
toward the sum, effectively counting the server.return sum( grid[i][j] and (row[i] > 1 or col[j] > 1) for i in range(m) for j in range(n) )
This code bypasses a separate conditional branching for each server and instead uses a compact generator expression to do the counting, which is a common Pythonic pattern for concise and readable code. The main algorithmic insight is that by keeping track of the row and column server counts, we can determine the capability of each server to communicate in constant time during the second iteration.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's assume we have a server grid grid
represented by the following 3 x 4
matrix:
[ [1, 0, 0, 1], [0, 1, 1, 0], [0, 0, 1, 0] ]
Let's walk through the solution step-by-step for this example:
-
Initialize two lists,
row
andcol
, each with values initialized at0
. For our grid,row
will have 3 elements andcol
will have 4 elements, corresponding to the number of rows and columns ingrid
, respectively.After initialization:
row = [0, 0, 0] col = [0, 0, 0, 0]
-
We iterate over each cell in the
grid
to count the number of servers in each row and column.After completing this step for our example, the
row
andcol
lists will look like this:row = [2, 2, 1] col = [1, 1, 2, 1]
The
row
list tells us that the first and second rows each have 2 servers, while the third row has 1 server. Thecol
list tells us that the first, second, and fourth columns each have 1 server, and the third column has 2 servers. -
Next, we perform a second iteration over the
grid
. This time, for each server, we check if it can communicate with others. Ifrow[i]
orcol[j]
is greater than 1, the server can communicate.- For
grid[0][0]
, we have a server androw[0] > 1
(becauserow[0]
is2
) - so this server can communicate. - For
grid[0][3]
, we have a server, but neitherrow[0] > 1
norcol[3] > 1
(becauserow[0]
is2
, butcol[3]
is1
) - so this server cannot communicate. - For
grid[1][1]
, we have a server and bothrow[1] > 1
andcol[1] > 1
, which means this server can communicate. - For
grid[1][2]
, we have a server andcol[2] > 1
, so this server can communicate. - For
grid[2][2]
, we do have a server but sincerow[2]
is not greater than 1, this server cannot communicate.
We continue performing this check for every cell in the grid that contains a server (
1
).Using the generator expression provided, we count the servers that can communicate, which evaluates to
3
for our example (grid[0][0]
,grid[1][1]
, andgrid[1][2]
). - For
Therefore, for the grid given in our example, the total number of servers that can communicate with at least one other server is 3.
Solution Implementation
1class Solution:
2 def count_servers(self, grid: List[List[int]]) -> int:
3 # Number of rows and columns in the grid
4 num_rows, num_cols = len(grid), len(grid[0])
5
6 # Arrays to keep track of the count of servers in each row and column
7 rows = [0] * num_rows
8 columns = [0] * num_cols
9
10 # First pass: count the number of servers in each row and column
11 for i in range(num_rows):
12 for j in range(num_cols):
13 if grid[i][j] == 1:
14 rows[i] += 1
15 columns[j] += 1
16
17 # Second pass: count the number of servers that can communicate
18 # Servers can communicate if they are in the same row or column with another server
19 server_count = 0
20 for i in range(num_rows):
21 for j in range(num_cols):
22 # Check if there's a server and if it's not the only server in its row or column
23 if grid[i][j] == 1 and (rows[i] > 1 or columns[j] > 1):
24 server_count += 1
25
26 return server_count
27
1class Solution {
2 public int countServers(int[][] grid) {
3 int numRows = grid.length; // Number of rows in the grid
4 int numCols = grid[0].length; // Number of columns in the grid
5
6 // Arrays to store the count of servers in each row and column
7 int[] rowCount = new int[numRows];
8 int[] colCount = new int[numCols];
9
10 // First iteration to fill in the rowCount and colCount arrays
11 for (int i = 0; i < numRows; ++i) {
12 for (int j = 0; j < numCols; ++j) {
13 // Count servers in each row and column
14 if (grid[i][j] == 1) {
15 rowCount[i]++;
16 colCount[j]++;
17 }
18 }
19 }
20
21 // Counter for the total number of connected servers
22 int connectedServers = 0;
23
24 // Second iteration to count the servers that can communicate
25 // Servers can communicate if they are not the only one in their row or column
26 for (int i = 0; i < numRows; ++i) {
27 for (int j = 0; j < numCols; ++j) {
28 if (grid[i][j] == 1 && (rowCount[i] > 1 || colCount[j] > 1)) {
29 connectedServers++;
30 }
31 }
32 }
33
34 // Return the number of connected servers
35 return connectedServers;
36 }
37}
38
1#include <vector>
2
3class Solution {
4public:
5 int countServers(std::vector<std::vector<int>>& grid) {
6 int rowCount = grid.size(); // number of rows in the grid
7 int colCount = grid[0].size(); // number of columns in the grid
8
9 // Create a vector to store the count of servers in each row and column
10 std::vector<int> serversInRow(rowCount, 0);
11 std::vector<int> serversInColumn(colCount, 0);
12
13 // Calculate the number of servers in each row and column
14 for (int i = 0; i < rowCount; ++i) {
15 for (int j = 0; j < colCount; ++j) {
16 if (grid[i][j]) { // if there is a server at position (i, j)
17 ++serversInRow[i]; // increment the count for the row
18 ++serversInColumn[j]; // increment the count for the column
19 }
20 }
21 }
22
23 // Count the number of servers that can communicate with at least one other server
24 int serverCount = 0; // variable to keep track of the total count
25 for (int i = 0; i < rowCount; ++i) {
26 for (int j = 0; j < colCount; ++j) {
27 // A server at (i, j) can communicate if there are other servers in the same
28 // row or column (hence, count will be more than 1 for that row or column).
29 if (grid[i][j] && (serversInRow[i] > 1 || serversInColumn[j] > 1)) {
30 serverCount++;
31 }
32 }
33 }
34
35 return serverCount; // Return the total count of servers that can communicate
36 }
37};
38
1function countServers(grid: number[][]): number {
2 // Get the number of rows (m) and columns (n) from the grid.
3 const rowCount = grid.length;
4 const colCount = grid[0].length;
5
6 // Initialize arrays to keep track of server counts in each row and column.
7 const rowServerCounts = new Array(rowCount).fill(0);
8 const colServerCounts = new Array(colCount).fill(0);
9
10 // First pass: Count the number of servers in each row and column.
11 for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
12 for (let colIndex = 0; colIndex < colCount; colIndex++) {
13 if (grid[rowIndex][colIndex] === 1) {
14 rowServerCounts[rowIndex]++;
15 colServerCounts[colIndex]++;
16 }
17 }
18 }
19
20 // Initialize a variable to keep track of the total number of connected servers.
21 let connectedServers = 0;
22
23 // Second pass: Determine if each server is connected horizontally or vertically.
24 for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
25 for (let colIndex = 0; colIndex < colCount; colIndex++) {
26 // If there's a server and there's more than one server in the current row or column,
27 // it is considered connected.
28 if (grid[rowIndex][colIndex] === 1 && (rowServerCounts[rowIndex] > 1 || colServerCounts[colIndex] > 1)) {
29 connectedServers++;
30 }
31 }
32 }
33
34 // Return the total count of connected servers.
35 return connectedServers;
36}
37
Time and Space Complexity
Time Complexity
The given code consists of two main parts: The first part is a double loop that goes through the entire grid to count the number of servers in each row and column. Since this loop goes through all the elements of the m x n
grid exactly once, the time complexity for this part is O(m * n)
.
The second part of the code calculates the sum with a generator expression that also iterates through every element in the grid. It checks if there's a server in the given cell (grid[i][j]
) and if there is more than one server in the corresponding row or column. Since this is done for each element, it also has a time complexity of O(m * n)
.
Therefore, the total time complexity of the entire function is O(m * n) + O(m * n)
, which simplifies to O(m * n)
because we only take the highest order term for big O notation.
Space Complexity
For space complexity, the code uses additional arrays row
and col
to store the counts of servers in each row and column, respectively. The size of row
is m
and the size of col
is n
. Hence, the extra space used is O(m + n)
.
In conclusion, the time complexity is O(m * n)
and the space complexity is O(m + n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?
Recommended Readings
https algomonster s3 us east 2 amazonaws com cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS
https algomonster s3 us east 2 amazonaws com cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell
Union Find Disjoint Set Union Data Structure Introduction Prerequisite Depth First Search Review problems dfs_intro Once we have a strong grasp of recursion and Depth First Search we can now introduce Disjoint Set Union DSU This data structure is motivated by the following problem Suppose we have sets of elements
Want a Structured Path to Master System Design Too? Don’t Miss This!