Skip to content

Commit 56e8dff

Browse files
authored
Merge pull request #115 from BrianLusina/feat/arrays-lucky-number
feat(arrays, puzzles, matrix): lucky number in a matrix
2 parents 6da8745 + 03e8a32 commit 56e8dff

24 files changed

+320
-0
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,8 @@
484484
* [Test Longest Increasing Subsequence](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/longest_increasing_subsequence/test_longest_increasing_subsequence.py)
485485
* Longest Subarray Of Ones
486486
* [Test Longest Subarray](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/longest_subarray_of_ones/test_longest_subarray.py)
487+
* Lucky Numbers In A Matrix
488+
* [Test Lucky Numbers In A Matrix](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/lucky_numbers_in_a_matrix/test_lucky_numbers_in_a_matrix.py)
487489
* Max Consecutive Ones
488490
* [Test Max Consecutive Ones](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/max_consecutive_ones/test_max_consecutive_ones.py)
489491
* Max Number Of Ksum Pairs
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Lucky Numbers in a Matrix
2+
3+
Given an m × n matrix of distinct numbers, return the lucky number in the matrix.
4+
5+
> A lucky number is an element of the matrix such that it is the smallest element in its row and largest in its column.
6+
7+
Constraints
8+
9+
- m = `matrix.length`
10+
- n = `matrix[i].length`
11+
- 1 <= m, n <= 50
12+
- 1 <= `matrix[i][j]` <= 10^5
13+
- All elements in the matrix are distinct.
14+
15+
## Examples
16+
17+
![Example 1](./images/examples/lucky_numbers_in_a_matrix_example_1.png)
18+
![Example 2](./images/examples/lucky_numbers_in_a_matrix_example_2.png)
19+
![Example 3](./images/examples/lucky_numbers_in_a_matrix_example_3.png)
20+
21+
## Solution: Greedy
22+
23+
The core idea behind the solution is to recognize that there can be, at most, one lucky number in the matrix. This is
24+
proven by contradiction, as having two such numbers would violate the unique conditions for being a lucky number.
25+
26+
> **Proof by contradiction:**
27+
>
28+
> Suppose we have an integer x located at row r1 and column c1 in a matrix. The integer x is the smallest value in its
29+
> row and the largest value in its column, making it a lucky number.
30+
> Now, assume another integer y exists in row r2 and column c2. For the sake of argument, let’s assume y is also a
31+
> lucky number, meaning it is the smallest value in its row and the largest in its column.
32+
> We assess these assumptions using the following steps:
33+
>
34+
> 1. As `y` is a lucky number, the smallest value is in row r2 and the largest value is in column c2. Let’s denote the
35+
> integer at position (r2,c2) as `a`
36+
> - Then, `y` < `a` because `y` is the minimum in its row
37+
> - Then, `x` > `a` because `x` is the maximum in it column.
38+
>
39+
> Therefore `y` < `x`
40+
>
41+
> 2. Next, let's consider the integer at position (r1, c2), which we'll call `b`
42+
> - Then, `y` > `b` because `y` is the maximum in its column
43+
> - Then, `x` < `b` because `x` is the minimum in its row
44+
>
45+
> Therefore `y` > `x`
46+
>
47+
> This leads to a contradiction, as we deduced `y<x` and `y>x`. This inconsistency implies that our initial assumption—
48+
> that y is a lucky number—is incorrect. Therefore, only x can be the lucky number in this configuration.
49+
>
50+
> Visually, this looks like this:
51+
>
52+
> ![Proof by contradiction](./images/solutions/lucky_numbers_proof_of_contradiction.png)
53+
54+
This problem can be solved using a greedy algorithm that analyzes the matrix row by row and column by column.
55+
56+
We start by iterating over the rows to find the minimum values. Out of those minimum values, we choose the largest
57+
minimum value and store it in r_largest_min. Similarly, we calculate the maximum values in columns and after finding
58+
all the largest values, we choose the smallest of them and store them in c_smallest_max. Once we have found the values
59+
in both rows and columns, we match them to see if they are the same. If they are, we return either of the values;
60+
r_largest_min or c_smallest_max. Otherwise if now matching value is found, we return an empty matrix.
61+
62+
Following are the detailed steps of the algorithm that we have just discussed:
63+
64+
1. We define two variables, r_largest_min and c_smallest_max:
65+
66+
- r_largest_min is set to negative infinity (float('-inf')) to ensure any row’s minimum value can be updated.
67+
- c_smallest_max is set to positive infinity (float('inf')) to ensure any column’s maximum value can be updated.
68+
69+
2. For each row in the matrix:
70+
71+
- We calculate the minimum value of the row (r_min).
72+
- Then, we update r_largest_min to the maximum of r_largest_min and r_min.
73+
- The steps above ensure we consider only minimum values in their rows, narrowing the candidate set for a lucky number.
74+
75+
3. For each column in the matrix:
76+
77+
- We calculate the maximum value of the column (c_max) by iterating over all rows.
78+
- Next, we update c_max_min to the minimum of c_smallest_max and c_max.
79+
- The above steps ensure we consider only maximum values in their columns, further narrowing the candidate set for a
80+
lucky number.
81+
82+
4. Finally, we compare whether r_largest_min equals c_smallest_max. If TRUE, we return the value stored in [r_largest_min].
83+
Otherwise, we return an empty array []. The comparison ensures that the identified value satisfies both conditions of
84+
being the minimum in its row and the maximum in its column, making it a valid lucky number.
85+
86+
![Solution 1](./images/solutions/lucky_numbers_in_a_matrix_solution_1.png)
87+
![Solution 2](./images/solutions/lucky_numbers_in_a_matrix_solution_2.png)
88+
![Solution 3](./images/solutions/lucky_numbers_in_a_matrix_solution_3.png)
89+
![Solution 4](./images/solutions/lucky_numbers_in_a_matrix_solution_4.png)
90+
![Solution 5](./images/solutions/lucky_numbers_in_a_matrix_solution_5.png)
91+
![Solution 6](./images/solutions/lucky_numbers_in_a_matrix_solution_6.png)
92+
![Solution 7](./images/solutions/lucky_numbers_in_a_matrix_solution_7.png)
93+
![Solution 8](./images/solutions/lucky_numbers_in_a_matrix_solution_8.png)
94+
![Solution 9](./images/solutions/lucky_numbers_in_a_matrix_solution_9.png)
95+
![Solution 10](./images/solutions/lucky_numbers_in_a_matrix_solution_10.png)
96+
![Solution 11](./images/solutions/lucky_numbers_in_a_matrix_solution_11.png)
97+
![Solution 12](./images/solutions/lucky_numbers_in_a_matrix_solution_12.png)
98+
![Solution 13](./images/solutions/lucky_numbers_in_a_matrix_solution_13.png)
99+
![Solution 14](./images/solutions/lucky_numbers_in_a_matrix_solution_14.png)
100+
![Solution 15](./images/solutions/lucky_numbers_in_a_matrix_solution_15.png)
101+
![Solution 16](./images/solutions/lucky_numbers_in_a_matrix_solution_16.png)
102+
103+
### Time Complexity
104+
105+
The time complexity of the solution is O(m×n), where m is the number of columns in the matrix and n is the number of
106+
rows in the matrix.
107+
108+
### Space Complexity
109+
110+
The solution’s space complexity is O(1) as no extra space is required apart from the few variables.
111+
112+
## Solution: Simulation
113+
114+
We are given a matrix of size MXN with distinct integers. We need to return the list of lucky numbers in the matrix.
115+
An integer in the matrix is lucky if it is the maximum integer in its column and it is the minimum value in its row.
116+
117+
In this approach, we will simulate the process by iterating over each integer in the matrix, checking if it is the
118+
maximum in its row and the minimum in its column. If it meets both criteria, we will add it to the list of lucky numbers,
119+
luckyNumbers.
120+
121+
The naive approach to check the criteria for each integer involves iterating over each integer in the current row and
122+
column to verify the minimum and maximum criteria, requiring M+N operations per integer. A more efficient method is to
123+
precompute the minimum of each row and the maximum of each column before processing the matrix. This allows us to check
124+
the criteria for each integer in constant time. We iterate over each row to store the minimum in rowMin and each column
125+
to store the maximum in colMax.
126+
127+
### Algorithm
128+
129+
1. Iterate over each row and store the minimum of the ith row at the ith position in the list rowMin.
130+
2. Iterate over each column and store the maximum of the ith column at the ith position in the list colMax.
131+
3. Iterate over each integer in the matrix and for each integer at (i, j), check if the integer is equal to rowMin[i]
132+
and colMax[j]. If yes, add it to the list luckyNumbers.
133+
4. Return luckyNumbers.
134+
135+
### Complexity Analysis
136+
137+
Here, N is the number of rows in the matrix and M is the number of columns in the matrix.
138+
139+
#### Time complexity: O(N*M).
140+
141+
To store the maximum of each row, we require N*M operations and the same for strong the maximum of each column. In the
142+
end, to find the lucky numbers we again iterate over each integer. Hence, the total time complexity is equal to O(N*M).
143+
144+
#### Space complexity: O(N+M).
145+
146+
We require two lists, rowMin and colMax of size N and M respectively. Hence the total space complexity is equal to O(N+M).
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
from typing import List
2+
3+
4+
def lucky_numbers(matrix: List[List[int]]) -> List[int]:
5+
"""
6+
This function takes a matrix as input and returns a list containing the lucky number(s) if they exist.
7+
8+
A lucky number is a number that is the maximum of the minimum values from each row and the minimum of the maximum
9+
values from each column.
10+
11+
If a lucky number exists, the function returns a list containing that number. Otherwise, it returns an empty list.
12+
13+
Time Complexity: O(m * n) where m is the number of columns and n is the number of rows in the matrix.
14+
Space Complexity: O(1) as we are using a constant amount of extra space.
15+
16+
Args:
17+
matrix (List[List[int]]): The input matrix.
18+
19+
Returns:
20+
List[int]: A list containing the lucky number(s) if they exist, otherwise an empty list.
21+
"""
22+
row_length = len(matrix)
23+
col_length = len(matrix[0])
24+
25+
# initialize a variable to keep track of the maximum of the minimum values from each row
26+
r_largest_min = float("-inf")
27+
28+
# We start by iterating over the rows to find the minimum values. Out of those minimum values, we choose the
29+
# largest minimum value and store it in r_largest_min
30+
for i in range(row_length):
31+
# find the minimum value in current row
32+
row_min = min(matrix[i])
33+
# update r_largest_min to be the maximum of current r_largest_min and the current row minimum
34+
r_largest_min = max(r_largest_min, row_min)
35+
36+
# initialize a variable to keep track of the minimum of the maximum values from each column
37+
c_smallest_max = float("inf")
38+
# Similarly, we calculate the maximum values in columns and after finding all the largest values, we choose the
39+
# smallest of them and store them in c_smallest_max
40+
for c in range(col_length):
41+
# find the maximum value in the current row
42+
col_max = max(matrix[r][c] for r in range(row_length))
43+
# update c_smallest_max to be the minium of the current c_smallest_max and the current column maximum
44+
c_smallest_max = min(c_smallest_max, col_max)
45+
46+
# If they are, we return either of the values; r_largest_min or c_smallest_max. Otherwise if now matching value is
47+
# found, we return an empty matrix.
48+
49+
# check if the maximum of row minima is equal to the minimum of column maxima
50+
if r_largest_min == c_smallest_max:
51+
# if they are equal, return a list containing the luky number
52+
return [r_largest_min]
53+
# Otherwise, return an empty list indicating no luky number exists
54+
return []
55+
56+
57+
def lucky_numbers_simulation(matrix: List[List[int]]) -> List[int]:
58+
"""
59+
This function takes a matrix as input and returns a list containing all the lucky number(s) if they exist.
60+
61+
A lucky number is a number that is the maximum of the minimum values from each row and the minimum of the maximum
62+
values from each column.
63+
64+
The function first calculates the minimum values from each row and the maximum values from each column, then compares
65+
these two lists to find the lucky number(s).
66+
67+
Time Complexity: O(m * n) where m is the number of columns and n is the number of rows in the matrix.
68+
Space Complexity: O(m + n) as we are using two lists of size m and n respectively.
69+
70+
Args:
71+
matrix (List[List[int]]): The input matrix.
72+
73+
Returns:
74+
List[int]: A list containing all the lucky number(s) if they exist, otherwise an empty list.
75+
"""
76+
row_length = len(matrix)
77+
col_length = len(matrix[0])
78+
79+
row_min = []
80+
for i in range(row_length):
81+
r_min = float('inf')
82+
for j in range(col_length):
83+
r_min = min(r_min, matrix[i][j])
84+
row_min.append(r_min)
85+
86+
col_max = []
87+
for i in range(col_length):
88+
c_max = float('-inf')
89+
for j in range(row_length):
90+
c_max = max(c_max, matrix[j][i])
91+
col_max.append(c_max)
92+
93+
result = []
94+
for i in range(row_length):
95+
for j in range(col_length):
96+
if matrix[i][j] == row_min[i] and matrix[i][j] == col_max[j]:
97+
result.append(matrix[i][j])
98+
99+
return result
31.4 KB
Loading
29.1 KB
Loading
27.5 KB
Loading
20.1 KB
Loading
37.2 KB
Loading
38.1 KB
Loading
37.6 KB
Loading

0 commit comments

Comments
 (0)