力扣 977. 有序数组的平方:双指针法的优雅解法

大家好!今天来分享力扣第 977 题 "有序数组的平方" 的解题思路。这道题看似基础,但想写出高效解法需要结合数组的特性做巧思 ------ 我们从暴力法的局限出发,一步步推导到最优的双指针解法。

一、题目描述

给你一个按非递减顺序排序 的整数数组 nums,返回每个数字的平方组成的新数组,要求新数组也按非递减顺序排序

示例

  • 输入:nums = [-4,-1,0,3,10]

  • 输出:[0,1,9,16,100]

  • 输入:nums = [-7,-3,2,3,11]

  • 输出:[4,9,9,49,121]

二、问题分析:暴力法的局限

首先想到的是暴力解法:先遍历数组计算每个元素的平方,再对结果排序。

暴力法代码如下:

cpp 复制代码
vector<int> sortedSquares(vector<int>& nums) {
    vector<int> res;
    for (int num : nums) res.push_back(num * num);
    sort(res.begin(), res.end());
    return res;
}

暴力法的优点是简单直观,但缺点也很明显:时间复杂度由排序操作主导,达到 O (n log n)(n 为数组长度)。当 n 很大时(比如 10^6 级别),排序的开销会显著拉低效率。

有没有办法避开排序,直接得到有序结果?

三、核心思路:双指针法的妙用

观察原数组的特性:数组是有序的,但负数的平方可能成为最大值 (比如 - 4 的平方 16 比 3 的平方 9 大)。因此,平方后的数组最大值一定出现在原数组的两端(要么最左,要么最右)。

基于这个发现,我们可以用双指针法从两端向中间遍历:

  1. 用指针i指向数组头部,指针j指向数组尾部;
  2. 用指针k指向结果数组的末尾(从后往前填充最大值);
  3. 比较nums[i]²nums[j]²,将较大值放入newNums[k],并移动对应的指针(i++j--),同时k--
  4. 重复上述步骤,直到i > j

四、双指针法代码实现与解读

结合这个思路,我们写出最优解代码(也是题目中给出的解法):

cpp 复制代码
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> newNums(nums.size()); // 初始化结果数组,大小与原数组一致
        int k = nums.size() - 1; // 结果数组的填充指针(从末尾开始)
        
        // 双指针:i指向头部,j指向尾部
        for (int i = 0, j = nums.size() - 1; i <= j; ) {
            if (nums[i] * nums[i] >= nums[j] * nums[j]) {
                newNums[k--] = nums[i] * nums[i]; // 头部平方更大,放入结果末尾
                i++; // 头部指针右移
            } else {
                newNums[k--] = nums[j] * nums[j]; // 尾部平方更大,放入结果末尾
                j--; // 尾部指针左移
            }
        }
        
        return newNums;
    }
};

代码解读:

  • 初始化结果数组newNums的大小与原数组相同,避免动态扩容的开销;
  • 双指针遍历ij分别从两端向中间靠拢,每次处理当前最大的平方值;
  • 从后往前填充k指针从结果数组末尾开始,保证每次放入的是当前最大的平方值,最终结果自然有序。

五、复杂度分析

  • 时间复杂度:O (n)。仅需遍历数组一次,每个元素处理一次;
  • 空间复杂度:O (n)。用于存储结果数组(若允许修改原数组,空间复杂度可优化为 O (1),但题目要求返回新数组)。

这是最优复杂度 ------ 因为至少需要遍历一次数组计算平方,时间复杂度不可能低于 O (n)。

六、总结

这道题的关键是利用原数组的有序性,意识到平方后的最大值出现在数组两端,从而用双指针法 "跳过排序步骤"。这种思路不仅适用于本题,还广泛应用于:

  • 两数之和 II(有序数组);
  • 合并两个有序数组;
  • 反转字符串中的元音字母等。

掌握 "有序数组 + 双指针" 的组合技巧,能大幅提升数组类题目的解题效率。如果有其他思路或疑问,欢迎评论交流!

相关推荐
-dzk-5 小时前
【代码随想录】LC 59.螺旋矩阵 II
c++·线性代数·算法·矩阵·模拟
风筝在晴天搁浅5 小时前
hot100 78.子集
java·算法
Jasmine_llq5 小时前
《P4587 [FJOI2016] 神秘数》
算法·倍增思想·稀疏表(st 表)·前缀和数组(解决静态区间和查询·st表核心实现高效预处理和查询·预处理优化(提前计算所需信息·快速io提升大规模数据读写效率
超级大只老咪5 小时前
快速进制转换
笔记·算法
m0_706653235 小时前
C++编译期数组操作
开发语言·c++·算法
故事和你916 小时前
sdut-Java面向对象-06 继承和多态、抽象类和接口(函数题:10-18题)
java·开发语言·算法·面向对象·基础语法·继承和多态·抽象类和接口
qq_423233906 小时前
C++与Python混合编程实战
开发语言·c++·算法
TracyCoder1236 小时前
LeetCode Hot100(19/100)——206. 反转链表
算法·leetcode
m0_715575346 小时前
分布式任务调度系统
开发语言·c++·算法