题目来源:🔒LeetCode 360:有序转化数组
问题抽象: 给定一个 有序整数数组 nums
(升序排列)和三个整数 a
、b
、c
,需对每个元素应用二次函数 f(x) = ax² + bx + c
并返回结果数组,且结果数组必须 非递减排序(升序)。
-
核心需求:
- 对每个
nums[i]
计算函数值f(nums[i])
; - 结果数组需满足
ans[0] ≤ ans[1] ≤ ... ≤ ans[n-1]
; - 利用二次函数 开口方向特性 优化排序过程(避免直接排序的
O(n log n)
时间)。
- 对每个
-
优化策略:
- 开口向上(a>0):函数值呈 中间小、两端大,使用双指针从数组两端向中间遍历,较大值逆序填入结果数组,最后反转得升序;
- 开口向下(a<0)或线性(a=0):函数值呈 中间大、两端小 或单调,使用双指针从两端向中间遍历,较小值正序填入结果数组(天然升序)。
-
计算约束:
- 时间复杂度 O(n):单次遍历完成(双指针移动
n
次); - 空间复杂度 O(n):存储结果数组(不含递归栈);
- 避免显式排序:利用二次函数单调性减少计算量。
- 时间复杂度 O(n):单次遍历完成(双指针移动
-
边界处理:
- 空数组:直接返回空数组;
- 单元素数组:直接返回
[f(nums[0])]
; - a=0 且 b=0:函数值恒为
c
,返回全c
数组; - a=0 但 b≠0:函数为线性,若
b>0
则结果升序,若b<0
则结果降序(需双指针处理); - 大数计算:结果值在 32 位整数范围内(题目保证)。
输入:有序整数数组 nums
(0 ≤ length ≤ 2×10^4
);系数 a
, b
, c
(值域 [-2^31, 2^31-1]
)。
输出:升序结果数组 ans
(长度同 nums
)。
解题思路
题目要求将有序数组根据二次函数 f(x) = ax² + bx + c
转换后,结果仍保持有序。解题关键在于利用二次函数的性质和数组有序的特性:
- 二次函数性质:
- 当
a > 0
时,抛物线开口向上,函数值在两端大、中间小。 - 当
a ≤ 0
时,抛物线开口向下或为线性函数,函数值在两端小、中间大。
- 当
- 双指针策略:
- 使用双指针从数组两端向中间遍历,根据
a
的正负决定填充顺序:- a > 0:结果数组从后向前填充(先放较大值),最后反转数组得到升序。
- a ≤ 0:结果数组从前向后填充(先放较小值),直接得到升序。
- 使用双指针从数组两端向中间遍历,根据
- 避免额外操作:
- 通过分情况处理,确保一次遍历完成,仅当
a > 0
时需反转数组,时间复杂度仍为 O(n)。
- 通过分情况处理,确保一次遍历完成,仅当
代码实现(Java版)🔥点击下载源码
class Solution {
public int[] sortTransformedArray(int[] nums, int a, int b, int c) {
int n = nums.length;
int[] res = new int[n];
int left = 0, right = n - 1;
// 计算函数值
Function<Integer, Integer> f = x -> a * x * x + b * x + c;
if (a > 0) {
// a>0:从后向前填充(结果数组为降序)
int idx = n - 1;
while (left <= right) {
int fLeft = f.apply(nums[left]);
int fRight = f.apply(nums[right]);
if (fLeft > fRight) {
res[idx--] = fLeft; // 左端值较大
left++;
} else {
res[idx--] = fRight; // 右端值较大
right--;
}
}
reverse(res); // 反转得到升序
} else {
// a<=0:从前向后填充(结果数组为升序)
int idx = 0;
while (left <= right) {
int fLeft = f.apply(nums[left]);
int fRight = f.apply(nums[right]);
if (fLeft < fRight) {
res[idx++] = fLeft; // 左端值较小
left++;
} else {
res[idx++] = fRight; // 右端值较小
right--;
}
}
}
return res;
}
// 反转数组
private void reverse(int[] arr) {
int i = 0, j = arr.length - 1;
while (i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
}
代码说明
- 函数计算:
- 使用
Function<Integer, Integer>
定义二次函数,避免重复计算逻辑。
- 使用
- 双指针遍历:
- a > 0:指针
idx
从数组末尾向前填充,每次取左右指针中较大的函数值,确保结果数组为降序。 - a ≤ 0:指针
idx
从数组开头向后填充,每次取左右指针中较小的函数值,确保结果数组为升序。
- a > 0:指针
- 反转操作:
- 仅当
a > 0
时调用reverse()
方法,将降序结果转为升序。
- 仅当
- 复杂度:时间复杂度:O(n),遍历数组一次,反转操作一次(O(n))。空间复杂度:O(n),存储结果数组,无额外空间。