121. Row and Column Totals
Problem
For a rectangular integer matrix, return (row_totals,column_totals). Each row total includes all its columns and each column total includes all its rows. Empty input returns two empty lists. A matrix with rows but zero columns has zero for each row total.
Examples
Example 1
matrix_totals([[1,2,3],[4,5,6]])
=> ([6, 15], [5, 7, 9])A nonsquare matrix distinguishes the two dimensions.
Example 2
matrix_totals([])
=> ([], [])Neither dimension has entries.
Example 3
matrix_totals([[],[]])
=> ([0, 0], [])Empty rows still have zero row totals.
Reveal the worked solution and reasoning
Why this mechanism works
Each cell contributes to exactly two independent summaries, one named by its row and one by its column. A single nested traversal can update both. The row and column indices are coordinates, while their corresponding accumulators have different output dimensions.
State and boundaries
row_totals has one accumulator per row; column_totals has one per column. Each includes exactly the cells consumed so far in that row or column.
Protocol
- Allocate zero accumulators for both dimensions.
- Visit every (r,c) cell.
- Add its value to row_totals[r] and column_totals[c].
- Return both lists.
Visualization
1 2 3 -> row total 6
4 5 6 -> row total 15
| | |
5 7 9 -> column totalsPython implementation
def matrix_totals(matrix):
rows = len(matrix)
cols = len(matrix[0]) if rows else 0
row_totals, column_totals = [0] * rows, [0] * cols
for r in range(rows):
for c in range(cols):
row_totals[r] += matrix[r][c]
column_totals[c] += matrix[r][c]
return row_totals, column_totals
Worked execution
matrix_totals([[1,2,3],[4,5,6]])
L2 matrix_totals: rows = len(matrix) matrix=[[1, 2, 3], [4, 5, 6]]; rows=2 L3 matrix_totals: cols = len(matrix[0]) if rows else 0 matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3 L4 matrix_totals: row_totals, column_totals = [0] * rows, [0] * cols matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[0, 0]; column_totals=[0, 0, 0] L5 matrix_totals: for r in range(rows): matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[0, 0]; column_totals=[0, 0, 0]; r=0 L6 matrix_totals: for c in range(cols): matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[0, 0]; column_totals=[0, 0, 0]; r=0; c=0 L7 matrix_totals: row_totals[r] += matrix[r][c] matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[1, 0]; column_totals=[0, 0, 0]; r=0; c=0 L8 matrix_totals: column_totals[c] += matrix[r][c] matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[1, 0]; column_totals=[1, 0, 0]; r=0; c=0 L6 matrix_totals: for c in range(cols): matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[1, 0]; column_totals=[1, 0, 0]; r=0; c=1 ... 11 intermediate state changes omitted ... L8 matrix_totals: column_totals[c] += matrix[r][c] matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[6, 9]; column_totals=[5, 7, 3]; r=1; c=1 L6 matrix_totals: for c in range(cols): matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[6, 9]; column_totals=[5, 7, 3]; r=1; c=2 L7 matrix_totals: row_totals[r] += matrix[r][c] matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[6, 15]; column_totals=[5, 7, 3]; r=1; c=2 L8 matrix_totals: column_totals[c] += matrix[r][c] matrix=[[1, 2, 3], [4, 5, 6]]; rows=2; cols=3; row_totals=[6, 15]; column_totals=[5, 7, 9]; r=1; c=2
Result: ([6, 15], [5, 7, 9]). A nonsquare matrix distinguishes the two dimensions.
matrix_totals([])
L2 matrix_totals: rows = len(matrix) matrix=[]; rows=0 L3 matrix_totals: cols = len(matrix[0]) if rows else 0 matrix=[]; rows=0; cols=0 L4 matrix_totals: row_totals, column_totals = [0] * rows, [0] * cols matrix=[]; rows=0; cols=0; row_totals=[]; column_totals=[]
Result: ([], []). Neither dimension has entries.
Why the solution is correct
Each cell is visited once and added once to each relevant accumulator. No other row or column receives that contribution. Therefore each final accumulator contains precisely the sum of its own cells.
Time and space
O(rows*cols) time and O(rows+cols) returned storage.
Common mistakes
Allocating column totals using the row count fails for nonsquare matrices. Swapping r and c can pass square examples while remaining wrong.
Reasoning card
State: row_totals has one accumulator per row; column_totals has one per column. Each includes exactly the cells consumed so far in that row or column.
Invariant: Each cell contributes once to its row and column accumulator.
Decision to rehearse: Reset row-local state; retain column-global state.
Finish: Return both lists
Transfer exercise
Also compute main and anti-diagonal totals for a square matrix without another traversal.