LeetCode 977. 有序数组的平方
题目描述

题意分析
给定一个按非递减顺序排列的整数数组,将每个元素平方,返回同样按非递减顺序排列的新数组。每次出现都要保留,原数组中的重复值或平方后相等的值,也要按原有数量写入结果。
原数组可能包含负数、零和正数。负数越小,绝对值可能越大,所以逐项平方并不能保证结果仍然有序。题目进阶要求线性时间,应利用输入已经有序这一条件,而不是平方后重新排序。
解法:双指针倒序填充
核心思路
[!blue]
平方的大小由绝对值决定。对于剩余的有序区间,若全是负数,最左端绝对值最大;若全是非负数,最右端最大;若跨过零,最大绝对值仍只能来自最左的负数或最右的正数。因此只比较两个端点的平方,就能找到剩余元素中的最大平方。
用
left、right表示尚未处理的区间,每次取较大的端点平方,放进结果最后一个空位置pos。因为取得的是最大值,写入方向必须从后向前;已经写好的后缀都是最终位置,不需要再参与比较。写入后只移动提供这个平方值的那个指针,表示消费了原数组中的一次出现,再将
pos向前移动。若两端平方相等,任选一端写入即可,另一个元素仍留在区间内等待后续处理,不能同时丢掉。每轮开始时,尚未处理的元素数量恰好等于结果空位置的数量。循环让
pos从n - 1走到0,一共写入n次;最后左右指针相遇时仍有一个值要处理。由每次把剩余最大值放到最右空位,最终结果自然非递减,也完整保留全部元素。
解题步骤
- 创建长度为
n的结果数组,初始化left = 0、right = n - 1。- 让写入位置
pos从n - 1递减到0,分别计算两端平方。- 较大平方写入
res[pos],只移动对应的端点;相等时任选一端。- 所有位置写满后返回结果数组,原数组不需要修改。
代码实现
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int[] res = new int[n];
int left = 0;
int right = n - 1;
for (int pos = n - 1; pos >= 0; pos--) {
int leftSquare = nums[left] * nums[left];
int rightSquare = nums[right] * nums[right];
if (leftSquare > rightSquare) {
// 最大平方值放到当前最后的空位。
res[pos] = leftSquare;
left++;
} else {
res[pos] = rightSquare;
right--;
}
}
return res;
}
}
func sortedSquares(nums []int) []int {
n := len(nums)
res := make([]int, n)
left := 0
right := n - 1
for pos := n - 1; pos >= 0; pos-- {
leftSquare := nums[left] * nums[left]
rightSquare := nums[right] * nums[right]
if leftSquare > rightSquare {
// 原数组两端产生当前最大的平方值。
res[pos] = leftSquare
left++
} else {
res[pos] = rightSquare
right--
}
}
return res
}
复杂度分析
- 时间复杂度:$O(n)$,每轮消费一个元素并填入一个结果位置,共执行
n轮。- 空间复杂度:返回数组占 $O(n)$;不计返回结果时,只使用两个端点和写入下标,辅助空间为 $O(1)$。
关键点总结
[!green]
- 有序区间的最大绝对值在两端,端点平方比较即可确定剩余最大值。
- 每次取得最大值,所以结果从右向左填充。
- 每轮只消费一个元素,即使两端平方相等也要分别保留其出现次数。
易错点总结
[!yellow]
- 直接比较原数值大小,会忽略负数绝对值较大时的平方。
- 把每次选出的较大值从前向后写,会得到降序结果。
- 两端平方相等时同时移动两个指针,却只写一个位置,会丢失一次出现。
- 使用
left < right就结束循环,会漏掉两指针相遇后的最后一个元素;按结果位置循环能自然覆盖它。- 直接在原数组末尾覆盖答案,可能覆盖还没有被消费的输入值;这里使用独立结果数组。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 360. 有序转化数组 | 中等 | 平方是二次函数特例,原题还需根据二次项符号选择从两端填最大还是最小值。 |
| 88. 合并两个有序数组 | 简单 | 平方后负数段逆序、非负段正序,可视为两条有序序列的归并。 |
| 补充题 160. 有序数组的不同平方值计数 | 中等 | 都比较有序数组两端的绝对值;补充题跳过相同绝对值,只统计不同平方。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!