有序数组的平方:双指针比排序快 10 倍的秘密

14 阅读3分钟

有序数组的平方:双指针比排序快 10 倍的秘密

从一个反直觉的现象说起

你有没有想过:把一个有序数组每个元素平方后,结果还是有序的吗?

直觉告诉你——当然不是。-4 平方是 16-1 平方是 116 > 1,顺序不就乱了吗?

但如果你仔细观察这个数组:-4, -1, 0, 3, 10

平方后:16, 1, 0, 9, 100

排序后:0, 1, 9, 16, 100

答案取决于你的数组长什么样——如果原数组包含负数,平方后负数会变成大正数,整体顺序确实会乱套。

LeetCode 第 977 题正是围绕这个场景展开:给你一个非递减顺序排序的整数数组,返回每个元素平方后组成的新数组,同样按非递减顺序排序。


最容易想到的解法:先平方再排序

let nums = [-4, -1, 0, 3, 10];
let new_nums = nums.map(n => n * n);  // 先平方
new_nums.sort((a, b) => a - b);       // 再排序
console.log(new_nums);  // [0, 1, 9, 16, 100]

复杂度:平方 O(n) + 排序 O(n log n),总体 O(n log n)

这没问题,简单直接,能通过大部分测试。但这道题真正想考你的,是能否做到 O(n)


真正优雅的解法:双指针从两端向中间

核心洞察

观察平方后的规律:

原数组-4-10310
平方后16109100

两端是最大的两个数! 因为绝对值越大,平方越大。

所以我们可以从数组两端开始,把较大的那个平方后放到结果数组的末尾,然后收缩指针,继续比较。

图解过程

原数组:  [-4, -1, 0, 3, 10]
            ↑           ↑
          left        right

Step 1: 10²=100 > 4²=16 → 放末尾 → [?, ?, ?, ?, 100]
Step 2: 3²=9 > 4²=16 → 放倒数第二 → [?, ?, ?, 9, 100]
Step 3: 4²=16 > 1²=1 → 放倒数第三 → [?, ?, 16, 9, 100]
Step 4: 1²=1 > 0²=0 → 放倒数第四 → [?, 1, 16, 9, 100]
Step 5: 0²=0 → 放第一位 → [0, 1, 16, 9, 100]

最终代码

// LeetCode 标准函数签名
var sortedSquares = function(nums) {
  let new_nums = [];
  let k = nums.length - 1;     // 结果数组的写入位置(从后往前)
  let left = 0;                // 左指针(负数端)
  let right = nums.length - 1; // 右指针(非负数端)

  while (k >= 0) {
    let leftSquare = nums[left] * nums[left];
    let rightSquare = nums[right] * nums[right];

    if (leftSquare > rightSquare) {
      new_nums[k] = leftSquare;
      left++;
    } else {
      new_nums[k] = rightSquare;
      right--;
    }
    k--;
  }

  return new_nums; // [0, 1, 9, 16, 100]
};

关键点

  • 结果数组从右往左填入,每次 k--
  • left 指向最小值(左端),right 指向最大值(右端)
  • 谁平方大,谁就放到 new_nums[k] 的位置

为什么它快?

解法时间复杂度空间复杂度思路
平方+排序O(n log n)O(1) 或 O(n)暴力枚举
双指针O(n)O(n)贪心策略

n = 1,000,000 时,双指针比排序快约 20 倍。这在面试中是绝对的加分项。


一句话总结

有序数组平方后,最小的在中间,最大的在两端。双指针从两端向中间走,把大的平方从后往前填,就是天然的排序。

这道题是双指针思想的经典入门题,核心在于利用数组本身的有序性——不需要额外的排序算法,只要动动指针,就能 O(n) 搞定。

学会这道题,你会发现后续很多类似题目(合并有序数组、盛水容器等)都是这个思路的变体。


相关题目推荐

  • 88. 合并两个有序数组
    1. 两数之和 II - 输入有序数组
    1. 盛最多水的容器