Problem overview
The method lowerBound receives a sorted array of numbers, nums, and a number, value. It returns the smallest index at which value could be inserted so that the array stays sorted.
In the first example, nums = [1, 2, 3, 5, 7] and value = 4. Inserting 4 at index 3 gives [1, 2, 3, 4, 5, 7], which is still sorted, so the answer is 3. In the second example, nums = [1, 2, 3] and value = 2. The 2 can go at index 1, in front of the existing 2, or at index 2, behind it. Both keep the array sorted, and the method must return the smaller one, 1.
The statement asks for O(n) time and O(1) extra space, so one pass over the array with a few variables is enough.
Approach: find the first element that is not smaller
Because nums is sorted, everything before the insertion point is smaller than value, and everything from the insertion point on is greater than or equal to it. So the answer is the index of the first element that is greater than or equal to value. Walk through the array from the left and return the first index where that is true.
If the walk ends without finding such an element, every element is smaller than value, so it belongs after the last one. That position is nums.length.
A linear scan is the simplest correct answer. Since the array is sorted, binary search could find the same index in O(log n) time, but this lesson practises looping over indices, so it scans.
Step by step
- Loop over the indices. The method returns a position, not an element, so the loop needs the index
i. It starts at 0 and runs whilei < nums.length. TypeScript infers thatiis anumberfrom its initial value, so no annotation is needed. - Compare and return. Inside the loop, check whether
nums[i] >= value. The first time this is true,iis the answer, andreturnends the method at once. Use>=and not>: whenvalueis already in the array, the index of its first occurrence is the smallest valid position. - Handle the no-match case. After the loop, return
nums.length. Reaching that line means no element was greater than or equal tovalue.
The solution in TypeScript
class Solution {
lowerBound(nums: number[], value: number): number {
for (let i = 0; i < nums.length; i++) {
if (nums[i] >= value) {
return i;
}
}
return nums.length;
}
}
A few details are worth knowing:
- Return from inside a loop. A
returnleaves the whole method, not just the current iteration. That is why the finalreturn nums.lengthonly runs when the loop finished without returning. - Types are only checked by the compiler. The annotations
number[]andnumberare erased when TypeScript compiles to JavaScript. They help you while writing the code but add nothing at run time. - Plain numbers. A TypeScript
numberis a double-precision float, so the comparison works for negative values and decimals too, and there is no integer overflow to worry about here. - The return type. The method returns an index, which is always a whole number between 0 and
nums.lengthinclusive. The valuenums.lengthis one past the last valid index, which is exactly what "insert at the end" means.
Complexity
Time: O(n). In the worst case, when value is larger than every element, the loop runs nums.length times and each iteration does one comparison. In the best case it returns at index 0.
Space: O(1). The method only keeps the loop variable i. It does not copy or change the array.
Edge cases and common mistakes
- Empty array. The loop never runs, and the method returns
nums.length, which is 0. Inserting into an empty array puts the value at index 0. - Value larger than everything.
[1, 2, 3]withvalue = 10returns3. - Value smaller than everything.
[1, 2, 3]withvalue = -4returns0. - Duplicates.
[1, 2, 2, 2, 3]withvalue = 2returns1, the first 2, and[2, 2, 2, 2]withvalue = 2returns0. - Using
>instead of>=. That version skips over equal elements and returns the index after the last one that matches. For[1, 2, 3]andvalue = 2it returns2instead of1. - Forgetting the final return. Without
return nums.length, the method returnsundefinedwhen every element is smaller, and with the lessons' compiler settings nothing warns you about it. - Returning the element. The task asks for a position. Returning
nums[i]instead ofigives a value from the array.
Testing
nums = [1, 2, 3, 5, 7],value = 4gives3.nums = [1, 2, 3],value = 2gives1.nums = [],value = 5gives0.nums = [1, 2, 3],value = 10gives3.nums = [1, 2, 2, 2, 3],value = 2gives1.