【中等】力扣算法题解析LeetCode360:有序转化数组

关注文末推广名片,即可免费获得本题测试源码

题目来源:🔒LeetCode 360:有序转化数组

问题抽象: 给定一个 有序整数数组 nums(升序排列)和三个整数 abc,需对每个元素应用二次函数 f(x) = ax² + bx + c 并返回结果数组,且结果数组必须 非递减排序(升序)。

  1. 核心需求

    • 对每个 nums[i] 计算函数值 f(nums[i])
    • 结果数组需满足 ans[0] ≤ ans[1] ≤ ... ≤ ans[n-1]
    • 利用二次函数 开口方向特性 优化排序过程(避免直接排序的 O(n log n) 时间)。
  2. 优化策略

    • 开口向上(a>0):函数值呈 中间小、两端大,使用双指针从数组两端向中间遍历,较大值逆序填入结果数组,最后反转得升序;
    • 开口向下(a<0)或线性(a=0):函数值呈 中间大、两端小 或单调,使用双指针从两端向中间遍历,较小值正序填入结果数组(天然升序)。
  3. 计算约束

    • 时间复杂度 O(n):单次遍历完成(双指针移动 n 次);
    • 空间复杂度 O(n):存储结果数组(不含递归栈);
    • 避免显式排序:利用二次函数单调性减少计算量。
  4. 边界处理

    • 空数组:直接返回空数组;
    • 单元素数组:直接返回 [f(nums[0])]
    • a=0 且 b=0:函数值恒为 c,返回全 c 数组;
    • a=0 但 b≠0:函数为线性,若 b>0 则结果升序,若 b<0 则结果降序(需双指针处理);
    • 大数计算:结果值在 32 位整数范围内(题目保证)。

输入:有序整数数组 nums0 ≤ length ≤ 2×10^4);系数 a, b, c(值域 [-2^31, 2^31-1])。
输出:升序结果数组 ans(长度同 nums)。


解题思路

题目要求将有序数组根据二次函数 f(x) = ax² + bx + c 转换后,结果仍保持有序。解题关键在于利用二次函数的性质和数组有序的特性:

  1. 二次函数性质
    • a > 0 时,抛物线开口向上,函数值在两端大、中间小。
    • a ≤ 0 时,抛物线开口向下或为线性函数,函数值在两端小、中间大。
  2. 双指针策略
    • 使用双指针从数组两端向中间遍历,根据 a 的正负决定填充顺序:
      • a > 0:结果数组从后向前填充(先放较大值),最后反转数组得到升序。
      • a ≤ 0:结果数组从前向后填充(先放较小值),直接得到升序。
  3. 避免额外操作
    • 通过分情况处理,确保一次遍历完成,仅当 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--;
        }
    }
}

代码说明

  1. 函数计算
    • 使用 Function<Integer, Integer> 定义二次函数,避免重复计算逻辑。
  2. 双指针遍历
    • a > 0:指针 idx 从数组末尾向前填充,每次取左右指针中较大的函数值,确保结果数组为降序。
    • a ≤ 0:指针 idx 从数组开头向后填充,每次取左右指针中较小的函数值,确保结果数组为升序。
  3. 反转操作
    • 仅当 a > 0 时调用 reverse() 方法,将降序结果转为升序。
  4. 复杂度时间复杂度:O(n),遍历数组一次,反转操作一次(O(n))。空间复杂度:O(n),存储结果数组,无额外空间。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

达文汐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值