Last Updated: March 29, 2026
We have a sorted array that might contain negative numbers. We need to square every element and return the result in sorted order. The tricky part is that squaring can flip the order. For example, -4 squared is 16, which is larger than 3 squared (9), even though -4 comes before 3 in the original array.
The key observation: in the original sorted array, the largest squares are at the two ends. The most negative numbers (far left) and the most positive numbers (far right) produce the biggest squares. The smallest squares are somewhere in the middle, near zero. This "U-shape" pattern in the squared values is exactly what makes the two-pointer technique work here.
1 <= nums.length <= 10^4 → With n up to 10,000, O(n^2) would mean 100 million operations, which is borderline. O(n log n) and O(n) are both comfortable.-10^4 <= nums[i] <= 10^4 → Squaring the max value gives 10^8, which fits comfortably in a 32-bit integer. No overflow concerns.nums is sorted in non-decreasing order → This is a massive hint. Sorted input almost always means we can exploit the ordering to avoid extra work. Think two pointers or binary search.The most obvious approach: square every element, then sort the result. We already know how to square numbers and how to sort arrays, so let's just combine the two.
This completely ignores the fact that the input is already sorted, which feels wasteful. But it works, and it's a clean baseline.
nums.Input:
After squaring each element:
After sorting the squared array:
This approach works but wastes the sorted order of the input by re-sorting from scratch.
Can we exploit the sorted input to build the squared result in order without needing a separate sort?
Here's the key insight: when you square a sorted array with negative numbers, the largest squared values are at the extremes. Consider [-4, -1, 0, 3, 10]. The squares are [16, 1, 0, 9, 100]. The biggest square (100) comes from the rightmost element. The second biggest (16) comes from the leftmost. The smallest squares cluster near the middle, around zero.
So the squared values form a "U-shape": big on both ends, small in the middle. This means we can use two pointers, one at each end, and compare their squares. Whichever is bigger goes into the result next. We fill the result array from the back (largest to smallest), since we're finding the largest values first.
The sorted input guarantees that absolute values decrease as you move from the edges toward the center (where the values are closest to zero). So at any point during the algorithm, the two pointers are always looking at the two candidates for the next-largest square. By comparing them and picking the bigger one, we're essentially performing a merge of two sorted sequences: the negative numbers (whose squares are sorted in decreasing order from left to right) and the positive numbers (whose squares are sorted in increasing order from left to right).
This is exactly the merge step from merge sort, except we're merging from the largest element down instead of from the smallest up. That's why we fill the result array from back to front.
left at the start (index 0) and right at the end (index n-1).nums[left]^2 with nums[right]^2.