力扣 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(有序数组);
  • 合并两个有序数组;
  • 反转字符串中的元音字母等。

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

相关推荐
搞笑症患者11 分钟前
压缩感知(Compressed Sensing, CS)
算法·最小二乘法·压缩感知·正交匹配追踪omp·迭代阈值it算法
im_AMBER15 分钟前
Leetcode 101 对链表进行插入排序
数据结构·笔记·学习·算法·leetcode·排序算法
快手技术33 分钟前
AAAI 2026|全面发力!快手斩获 3 篇 Oral,12 篇论文入选!
前端·后端·算法
颜酱34 分钟前
前端算法必备:滑动窗口从入门到很熟练(最长/最短/计数三大类型)
前端·后端·算法
做科研的周师兄36 分钟前
【MATLAB 实战】栅格数据 K-Means 聚类(分块处理版)—— 解决大数据内存溢出、运行卡顿问题
人工智能·算法·机器学习·matlab·kmeans·聚类
X在敲AI代码37 分钟前
leetcodeD3
数据结构·算法
踩坑记录38 分钟前
leetcode hot100 560.和为 K 的子数组 medium 前缀和 + 哈希表
leetcode
码农小韩44 分钟前
基于Linux的C++学习——循环
linux·c语言·开发语言·c++·算法
CoderCodingNo1 小时前
【GESP】C++五级/四级练习(双指针/数学) luogu-P1147 连续自然数和
开发语言·c++·算法
颜酱1 小时前
前端算法必备:双指针从入门到很熟练(快慢指针+相向指针+滑动窗口)
前端·后端·算法