|
1 | 1 | # [Problem 624: Maximum Distance in Arrays](https://leetcode.com/problems/maximum-distance-in-arrays/description/?envType=daily-question)
|
2 | 2 |
|
3 | 3 | ## 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. |
4 | 12 |
|
5 | 13 | ## 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 | + |
7 | 51 | ## Attempted solution(s)
|
8 | 52 | ```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 | + |
| 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) |
11 | 131 | ```
|
| 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 | + |
| 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 | + |
| 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