You want to build some obstacle courses. You are given a 0-indexed integer array obstacles
of length n
, where obstacles[i]
describes the height of the ith
obstacle.
For every index i
between 0
and n - 1
(inclusive), find the length of the longest obstacle course in obstacles
such that:
0
and i
inclusive.ith
obstacle in the course.obstacles
.Return an array ans
of length n
, where ans[i]
is the length of the longest obstacle course for index i
as described above.
Example 1:
Input: obstacles = [1,2,3,2] Output: [1,2,3,3] Explanation: The longest valid obstacle course at each position is: - i = 0: [1], [1] has length 1. - i = 1: [1,2], [1,2] has length 2. - i = 2: [1,2,3], [1,2,3] has length 3. - i = 3: [1,2,3,2], [1,2,2] has length 3.
Example 2:
Input: obstacles = [2,2,1] Output: [1,2,1] Explanation: The longest valid obstacle course at each position is: - i = 0: [2], [2] has length 1. - i = 1: [2,2], [2,2] has length 2. - i = 2: [2,2,1], [1] has length 1.
Example 3:
Input: obstacles = [3,1,5,6,4,2] Output: [1,1,2,3,2,2] Explanation: The longest valid obstacle course at each position is: - i = 0: [3], [3] has length 1. - i = 1: [3,1], [1] has length 1. - i = 2: [3,1,5], [3,5] has length 2. [1,5] is also valid. - i = 3: [3,1,5,6], [3,5,6] has length 3. [1,5,6] is also valid. - i = 4: [3,1,5,6,4], [3,4] has length 2. [1,4] is also valid. - i = 5: [3,1,5,6,4,2], [1,2] has length 2.
Constraints:
n == obstacles.length
1 <= n <= 105
1 <= obstacles[i] <= 107
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to this obstacle course problem is like trying every single possible path for each obstacle. We'll consider all possible sub-courses that end at each position, and keep the best one seen so far.
Here's how the algorithm would work step-by-step:
def longest_obstacle_course_brute_force(obstacles):
number_of_obstacles = len(obstacles)
longest_courses = []
for current_obstacle_index in range(number_of_obstacles):
max_course_length = 0
# Iterate through all sub-courses ending at the current obstacle
for sub_course_end_index in range(current_obstacle_index + 1):
sub_course = obstacles[:sub_course_end_index + 1]
is_valid = True
# Check if the current sub-course is monotonically increasing
for i in range(len(sub_course) - 1):
if sub_course[i] > sub_course[i + 1]:
is_valid = False
break
if is_valid:
# Record the length of the valid sub-course
max_course_length = max(max_course_length, len(sub_course))
longest_courses.append(max_course_length)
return longest_courses
We want to find the length of the longest obstacle course ending at each obstacle. The key is to efficiently maintain a sorted list of the tallest obstacles we've seen so far and use that to determine the length of the valid course.
Here's how the algorithm would work step-by-step:
def longest_obstacle_course_at_each_position(obstacles):
longest_courses = []
result = []
for obstacle in obstacles:
# Find the smallest obstacle in longest_courses >= current obstacle
left, right = 0, len(longest_courses)
while left < right:
mid = (left + right) // 2
if longest_courses[mid] <= obstacle:
left = mid + 1
else:
right = mid
# If obstacle extends the longest course, add it
if left == len(longest_courses):
longest_courses.append(obstacle)
else:
# Otherwise, replace a larger obstacle with the current one
longest_courses[left] = obstacle
# The length of the longest course ending at current obstacle is left + 1
result.append(len(longest_courses))
return result
Case | How to Handle |
---|---|
Empty input array | Return an empty array since there are no obstacles to form a course. |
Array with a single obstacle | Return an array containing only '1' as the longest valid obstacle course is of length 1. |
Array with obstacles of decreasing height | The longest valid course at each position will always be '1' since each obstacle breaks the increasing sequence. |
Array with all obstacles of the same height | The longest valid course at each position will be the index + 1. |
Large input array exceeding memory limits | The algorithm should be memory-efficient, ideally O(n) space, to handle reasonably large inputs without crashing. |
Obstacle heights with extreme values (very large or very small) | Ensure the chosen data type (e.g., int) can accommodate the full range of obstacle heights to avoid overflow or underflow. |
Array with duplicate obstacle heights interspersed throughout | The binary search and tail array update mechanism should handle duplicates correctly, extending the increasing subsequence as needed. |
Obstacle heights are non-integer values (floating point) | If floats are permitted, ensure comparison logic accounts for potential floating-point precision errors. |