The problem
You get an integer array nums sorted in non-decreasing order. It may contain negatives. Return a new array of each number squared, also sorted in non-decreasing order.
Input: nums = [-4, -1, 0, 3, 10]
Output: [0, 1, 9, 16, 100]The squares are 16, 1, 0, 9, 100; sorted, they become 0, 1, 9, 16, 100.
How to think about it
Start with brute force. Square every element and sort the result. It’s one line and runs in O(n log n). It’s correct, but it throws away the fact that the input is already sorted, and the interviewer will ask for O(n).
Why the order breaks. Squaring keeps the order of the non-negatives but reverses the order of the negatives: -4 is the smallest number but has a bigger square than -1. So the squares go down and then up, a “valley” shape.
The key insight: the largest square is always at one of the two ends. The most negative number sits on the far left and the most positive on the far right, and one of them has the biggest absolute value. So compare the two ends, take the bigger square, and place it at the back of the result. Then move that pointer inward and repeat. You’re building the answer from largest to smallest.
Which pattern is this? Converging two pointers. The tell is “sorted input where the extremes matter”, here the two ends compete for the largest value.
Approach
- Create
resof sizen, withl = 0andr = n - 1. - Walk a write index
kfromn - 1down to 0. - If
|nums[l]| > |nums[r]|, putnums[l]²atres[k]and movelright. - Otherwise put
nums[r]²atres[k]and moverleft. - Return
res.
Solution
def sorted_squares(nums):
n = len(nums); res = [0]*n; l, r = 0, n-1
for k in range(n-1, -1, -1): # fill largest slot first
if abs(nums[l]) > abs(nums[r]): # bigger magnitude wins slot k
res[k] = nums[l]*nums[l]; l += 1
else:
res[k] = nums[r]*nums[r]; r -= 1
return res
# sorted_squares([-4,-1,0,3,10]) -> [0,1,9,16,100]Dry run
On nums = [-4, -1, 0, 3, 10]:
k |
l |
r |
nums[l] vs nums[r] |
Placed | res after |
|---|---|---|---|---|---|
| 4 | 0 | 4 | -4 vs 10 | 100 | [0, 0, 0, 0, 100] |
| 3 | 0 | 3 | -4 vs 3 | 16 | [0, 0, 0, 16, 100] |
| 2 | 1 | 3 | -1 vs 3 | 9 | [0, 0, 9, 16, 100] |
| 1 | 1 | 2 | -1 vs 0 | 1 | [0, 1, 9, 16, 100] |
| 0 | 2 | 2 | 0 vs 0 | 0 | [0, 1, 9, 16, 100] |
The answer is [0, 1, 9, 16, 100].
Complexity
- Time: O(n). Each step fills one slot and moves one pointer.
- Space: O(n) for the output array. Apart from that, only a few integers.
Edge cases and pitfalls
- All non-negative (
[1, 2, 3]): the right pointer wins every time and the order is preserved. - All negative (
[-3, -2, -1]): the left pointer wins every time and the order reverses. - Single element: one iteration with
l == r. - Filling from the front doesn’t work. The smallest square is somewhere in the middle, not at an end, so you can’t find it with two end pointers.
- Ties (
-3and3): either side is fine; the other one gets placed next.
Related problems
- Two Sum II – Input Array Is Sorted (LeetCode 167): converging pointers on a sorted array, deciding which end moves.
- Container With Most Water (LeetCode 11): another “compare the two ends, move one” argument.
- Merge Two Sorted Lists (LeetCode 21): this problem is really a merge of two sorted runs (the negatives reversed and the non-negatives).
- 1.Valid Palindrome (LeetCode 125)
- 2.Remove Duplicates from Sorted Array (LeetCode 26)
- 3.Move Zeroes (LeetCode 283)
- 4.Squares of a Sorted Array (LeetCode 977)
- 5.Backspace String Compare (LeetCode 844)
- 6.Two Sum II – Input Array Is Sorted (LeetCode 167)
- 7.3Sum (LeetCode 15)
- 8.3Sum Closest (LeetCode 16)
- 9.4Sum (LeetCode 18)
- 10.Container With Most Water (LeetCode 11)
- 11.Sort Colors (LeetCode 75)
- 12.Trapping Rain Water (LeetCode 42)