Skip to content

Commit 2d1c214

Browse files
My solution to 624
1 parent 60dabe7 commit 2d1c214

File tree

1 file changed

+167
-3
lines changed

1 file changed

+167
-3
lines changed

problems/624/jeremymanning.md

+167-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,175 @@
11
# [Problem 624: Maximum Distance in Arrays](https://leetcode.com/problems/maximum-distance-in-arrays/description/?envType=daily-question)
22

33
## Initial thoughts (stream-of-consciousness)
4+
- Since the arrays are sorted, we know that only the first and last elements in each array matter
5+
- The brute force solution would be:
6+
- Compute the minimums and maximums of each array: `bounds = [[a[0], a[-1]] for a in arrays]`
7+
- For each minimum, compute the maximum (from the other arrays) that's farthest away
8+
- This requires $O(m^2)$ time (for the $m$ arrays), which is likely intractable since $m$ could be as large as $10^5$.
9+
- Another possibility would be to tackle this in a dynamic programming way. What if we keep track of the *two* largest and smallest values, along with the array indices they come from?
10+
- The smallest minimum + the largest maximum from any other array. If a new minimum is found, we can now potentially update the largest maximum if it happened to be from the same array as the previous smallest minimum.
11+
- The largest maximum + the smallest minimum from any other array. If a new maximum is found, we can now potentially update the smallest minimum if it happened to be from the same array as the previous largest maximum.
412

513
## Refining the problem, round 2 thoughts
6-
14+
- Some things to work out:
15+
- How do we track the *two* smallest minima or the two largest maxima? We could initialize them to the first two minima/maxima (from the first two arrays). Then if we find a smaller minimum or a larger maximum in a future array, we can either replace the second smallest/largest or add the new values to the front and remove the second smallest/largest.
16+
- If the "next" array in the sequence has *both* a larger maximum *and* a smaller minimum, then we can only update *either* the maxima or the minima. We should choose whichever results in the larger absolute difference.
17+
- This makes me realize that we probably only need to keep track of the *single* smallest minimum and the *single* largest maximum, since if we just consider each array in turn it's not possible for a previous max/min to have come from the new array we're considering.
18+
- We just need to initialize the max and min using the first two arrays (based on what yields the largest absolute difference)
19+
- Then for each successive array, `a`:
20+
- If `a[0] < current_min and a[-1] > current_max`:
21+
- If abs(a[0] - current_max) > abs(current_min - a[-1]):
22+
- `current_min = a[0]`
23+
- Else:
24+
- `current_max = a[-1]`
25+
- Else:
26+
- If `a[0] < current_min`, `current_min = a[0]`
27+
- If `a[-1] > current_max`, `current_max = a[-1]`
28+
- Then we just return `abs(current_max - current_min)`
29+
- One potential edge case: what if we're in that first condition (where the new array, `i`, has *both* the new minimum and maximum values). We only get to replace the current min *or* max. Suppose we replace the min, since that results in a larger difference. But later on in the sequence, for some further-along array `j`, suppose we find an even *smaller* minimum (i.e., `j[0] < i[0]`), but no array after `i` has a larger value than `i[-1]`. Now we've missed out on a larger absolute difference, since we chose the wrong replacement of the `current_min` and `current_max` when we got to array `i`. So actually, we probably *do* need to keep track of those sorts of discarded options:
30+
- We could initialize `discarded_min = float('inf')` and `discarded_max = float('-inf')`. Then we could update the logic of the first case above as follows:
31+
- for each successive array, `a`:
32+
- If `a[0] < current_min and a[-1] > current_max`:
33+
- If abs(a[0] - current_max) > abs(current_min - a[-1]):
34+
- `current_min = a[0]`
35+
- `discarded_max = a[-1]`
36+
- Else:
37+
- `current_max = a[-1]`
38+
- `discarded_min = a[0]`
39+
- Else:
40+
- If `a[0] <= current_min`:
41+
- `current_min = a[0]`
42+
- If `discarded_max > current_max`:
43+
- `current_max = discarded_max`
44+
- If `a[-1] >= current_max`:
45+
- `current_max = a[-1]`
46+
- If `discarded_min < current_min`:
47+
- `current_min = discarded_min`
48+
- Now return `abs(current_max - current_min)`
49+
- Ok...I'm not 100% confident in this approach, but let's try it...
50+
751
## Attempted solution(s)
852
```python
9-
class Solution: # paste your code here!
10-
...
53+
class Solution:
54+
def maxDistance(self, arrays: List[List[int]]) -> int:
55+
discarded_min, discarded_max = float('inf'), float('-inf')
56+
if abs(arrays[0][0] - arrays[1][-1]) > abs(arrays[0][-1] - arrays[1][0]):
57+
current_min, current_max = arrays[0][0], arrays[1][-1]
58+
else:
59+
current_min, current_max = arrays[1][0], arrays[0][-1]
60+
if abs(arrays[0][0] - arrays[1][-1]) == abs(arrays[0][-1] - arrays[1][0]):
61+
discarded_min, discarded_max = arrays[0][0], arrays[1][-1]
62+
63+
for a in arrays[2:]:
64+
if a[0] < current_min and a[-1] > current_max:
65+
if abs(a[0] - current_max) > abs(current_min - a[-1]):
66+
current_min = a[0]
67+
discarded_max = a[-1]
68+
else:
69+
current_max = a[-1]
70+
discarded_min = a[0]
71+
else:
72+
if a[0] <= current_min:
73+
current_min = a[0]
74+
if discarded_max > current_max:
75+
current_max = discarded_max
76+
if a[-1] >= current_max:
77+
current_max = a[-1]
78+
if discarded_min < current_min:
79+
current_min = discarded_min
80+
81+
return abs(current_max - current_min)
82+
```
83+
- Given test cases pass
84+
- Let's generate some random new arrays:
85+
- `arrays = [[-88, -37, -32, -24, -5], [-59, 49, 53], [-31, 18, 34, 52, 59], [-7, 74], [-92, 4], [-31, 23], [-86, -12, 63, 67], [-66, -48, -12, 89], [-57, -16, -10, -5], [-91, -60, 65, 77, 85], [-61, -18, 53], [-50, -22], [-41, -32, 28, 47, 90], [-34, -11, 1, 35, 47], [-3, 11, 93, 95], [-19], [-63, -32, 7], [56]]`: pass
86+
- `arrays = [[-57, -27], [-92], [-86, 66], [-50, -21, -4, 23], [-96, 55, 63, 88], [-66, 81], [-94, -79, 4, 35], [91, 96], [-30, -3, 14, 55, 66], [56], [-5], [-95, -61, 50, 63], [-23, 29, 35], [-55, 87], [-99, -48, 34, 71, 97], [-97, -49, 52, 53], [-84, -41, 19, 28], [-78]]`: pass
87+
- `arrays = [[-51, -5, 16, 72, 89], [-94, -68, 13], [-100, -73], [-80, -48, -17, -7, 47], [-17, 51], [-88, -6]]`: pass
88+
- Ok...let's submit!
89+
90+
![Screenshot 2024-08-15 at 11 18 37 PM](https://github.com/user-attachments/assets/f76a11c8-7a27-4846-9030-5fdac59b0cbb)
91+
92+
Bummer...it fails for `arrays = [[1,3],[-10,-9,2,2,3,4],[-8,-5,2],[-10,-6,-5,-5,0,3],[-8,-6,-2,0,2,3,3],[-10,-10,-5,0]]`. I can see that for this one there are three arrays where the minimum is -10. The max across all arrays is 4, but that includes one of those -10 arrays. What I was hoping would happen is:
93+
- Given the first two arrays, intialize: `current_min, current_max, discarded_min, discarded_max = -10, 3, float(`-inf`), 4`
94+
- But I'm seeing that this actually isn't what the start of my code does...let's see if we can patch it up a bit.
95+
```python
96+
class Solution:
97+
def maxDistance(self, arrays: List[List[int]]) -> int:
98+
discarded_min, discarded_max = float('inf'), float('-inf')
99+
if abs(arrays[0][0] - arrays[1][-1]) > abs(arrays[0][-1] - arrays[1][0]):
100+
current_min, current_max = arrays[0][0], arrays[1][-1]
101+
if arrays[1][0] < current_min:
102+
dicarded_min = arrays[1][0]
103+
if arrays[0][-1] > current_max:
104+
discarded_max = arrays[0][-1]
105+
else:
106+
current_min, current_max = arrays[1][0], arrays[0][-1]
107+
if arrays[0][0] < current_min:
108+
dicarded_min = arrays[0][0]
109+
if arrays[1][-1] > current_max:
110+
discarded_max = arrays[1][-1]
111+
112+
for a in arrays[2:]:
113+
if a[0] < current_min and a[-1] > current_max:
114+
if abs(a[0] - current_max) > abs(current_min - a[-1]):
115+
current_min = a[0]
116+
discarded_max = a[-1]
117+
else:
118+
current_max = a[-1]
119+
discarded_min = a[0]
120+
else:
121+
if a[0] <= current_min:
122+
current_min = a[0]
123+
if discarded_max > current_max:
124+
current_max = discarded_max
125+
if a[-1] >= current_max:
126+
current_max = a[-1]
127+
if discarded_min < current_min:
128+
current_min = discarded_min
129+
130+
return abs(current_max - current_min)
11131
```
132+
- Ok, that fixes that test case
133+
- But...now a new case is failing (`arrays = [[-10,-9,-9,-3,-1,-1,0],[-5],[4],[-8],[-9,-6,-5,-4,-2,2,3],[-3,-3,-2,-1,0]]`):
134+
135+
![Screenshot 2024-08-15 at 11 27 01 PM](https://github.com/user-attachments/assets/af2ff6e8-e5ea-4608-bbc0-a947ada9db0b)
136+
137+
138+
- I can see a few things:
139+
- There are a bunch of single-element arrays, so that might be messing with the logic I've used:
140+
- Do I ever need to ensure that the same single element isn't used as *both* the min and max? I don't think so, since we're only selecting at most one element from a given array anyway.
141+
- But maybe the initial logic where I set `discarded_min` and `discarded_max` is still faulty? I think this may be a problem-- I forgot to account for the case where `abs(arrays[0][0] - arrays[1][-1]) == abs(arrays[0][-1] - arrays[1][0])`. If so, then...what do we do?
142+
- Actually maybe there's an even simpler solution:
143+
- initialize `current_min, current_max, max_dist = arrays[0][0], arrays[0][-1], 0`
144+
- Now loop through the remaining arrays:
145+
- Replace `max_dist` with `max(max_dist, abs(a[-1] - current_min), abs(a[0] - current_max))`
146+
- Replace `current_min` with `min(current_min, a[0])`
147+
- Replace `current_max` with `max(current_max, a[-1])`
148+
- Then just return `max_dist`
149+
- Now we don't need to deal with these edge cases.
150+
- Let's try it...
151+
152+
```python
153+
class Solution:
154+
def maxDistance(self, arrays: List[List[int]]) -> int:
155+
current_min, current_max, max_dist = arrays[0][0], arrays[0][-1], 0
156+
for a in arrays[1:]:
157+
max_dist = max(max_dist, abs(a[-1] - current_min), abs(a[0] - current_max))
158+
current_min = min(current_min, a[0])
159+
current_max = max(current_max, a[-1])
160+
return max_dist
161+
```
162+
- Now the previously failing test cases pass
163+
- Let's try a few more:
164+
- `arrays = [[79], [-59, 35], [-58, 81], [-100, -73, 17, 58], [-66, -31, -9, -2], [20, 37, 39], [-93, -43, -6, 27, 53], [-30], [-59, -18, 8, 59], [-68, -67, -31, -13, 66], [-65, -50, 99], [-70, -56, 41, 70], [-96, -90, 56], [-79, 23, 39, 78], [-77, -52, 45]]`: pass
165+
- `arrays = [[-20, 1, 25, 88, 94], [-53, -47, 88], [-84, -30, -19, 16, 95], [-27, 4, 46, 79], [-87, -79, -47, -23, 43], [-66, -51, 31, 85], [-20], [-54, 29], [-100, 1, 51, 98], [-89, -84, 15], [49], [-95, 18], [-83, -80, -25, 83, 91], [-25, 52], [-30], [-71, -8, 81, 92, 100]]`: pass
166+
- Ok...let's submit again...
167+
168+
![Screenshot 2024-08-15 at 11 40 57 PM](https://github.com/user-attachments/assets/cffeb74d-cbc0-44a2-be05-504570150a5c)
169+
170+
Solved!
171+
172+
Some post-mortem reflections on this one:
173+
- I was a bit too eager to finish this quickly, which resulted in submitting too early before I had fully thought through the logic or potential edge cases. Ironically this resulted in the full thing taking *longer* than I think it could have otherwise.
174+
- I sort of used the given test cases to "debug," but this seems akin to "overfitting" (maybe not *quite* cheating outright, but feels close!)
175+
- It turns out this was a pretty simply problem after I took a step back and stopped over-thinking it. Good to keep in mind for the future!

0 commit comments

Comments
 (0)