Given an integer array nums
sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order. Can you come up with an efficient solution for this problem?
For example:
nums = [-4, -1, 0, 3, 10]
should return [0, 1, 9, 16, 100]
nums = [-7, -3, 2, 3, 11]
should return [4, 9, 9, 49, 121]
Here are the constraints:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums
is sorted in non-decreasing order.What are the edge cases to consider? How would you optimize the solution for the best time complexity? Squaring each element and then sorting is a trivial solution. Can you come up with an O(n) solution?
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:
We need to find the squares of numbers and arrange them in ascending order. The brute force approach is straightforward: we will square each number individually and then sort the resulting list of squares.
Here's how the algorithm would work step-by-step:
def squares_of_a_sorted_array_brute_force(numbers):
squared_numbers = []
# Calculate the square of each number
for number in numbers:
square = number * number
squared_numbers.append(square)
# Sorting will arrange the squares in ascending order
squared_numbers.sort()
return squared_numbers
The key idea is to avoid sorting after squaring. Since the original numbers are sorted, the largest squares will be at the extreme ends. We use two pointers, one at each end, and work inwards, placing the larger square into the result.
Here's how the algorithm would work step-by-step:
def sorted_squares(numbers):
left_pointer = 0
right_pointer = len(numbers) - 1
results = [0] * len(numbers)
result_index = len(numbers) - 1
# Iterate until the pointers meet
while left_pointer <= right_pointer:
left_square = numbers[left_pointer] ** 2
right_square = numbers[right_pointer] ** 2
# Place larger square in result and adjust the appropriate pointer.
if left_square > right_square:
results[result_index] = left_square
left_pointer += 1
else:
results[result_index] = right_square
right_pointer -= 1
# The result array is populated from the end.
result_index -= 1
return results
Case | How to Handle |
---|---|
Null or empty input array | Return an empty array or throw an IllegalArgumentException, depending on the specification. |
Array with a single element | Square the element and return a new array containing only that squared value. |
Array with all zero values | Return an array of the same size filled with zeros. |
Array with all negative values | The squares will be positive and in decreasing order originally, so the algorithm must handle reversing the order or use a two-pointer approach. |
Array with all positive values | The squares will be positive and already in non-decreasing order, so the algorithm should maintain this order. |
Array with mixed positive and negative values | The algorithm should correctly handle the transition from decreasing squares (of negative numbers) to increasing squares (of positive numbers) by comparing values appropriately. |
Integer overflow when squaring large numbers | Use a data type that can accommodate larger values (e.g., long) or perform explicit overflow checks. |
Maximum size array (scalability) | The chosen algorithm (likely two-pointer) should perform efficiently in terms of time and space complexity, preferably O(n). |