题目描述

:::fold-green 相关原题

LeetCode 原题: ✅ 977. 有序数组的平方

:::

给定非降序的 32 位整数数组 nums,返回所有元素平方后不同数值的个数。

要求时间复杂度为 O(n),额外空间为 O(1),且不修改数组。

示例 1:

输入: nums = [-3,-1,-1,0,1,3,4]
输出: 4
解释: 平方后的不同值是 0、1、9、16;正负同绝对值及重复元素只计一次。

提示:

  • 输入为非降序的 32 位整数数组。
  • 要求 O(n) 时间、O(1) 额外空间。
  • 不能修改输入。

题意分析

两个整数平方相等,当且仅当它们的绝对值相等。只计不同平方值的数量,因此无需真的相乘或创建平方数组;利用原数组有序性,从两端处理绝对值即可。

解法:按两端绝对值整段去重

核心思路

[!blue]

剩余区间的最大绝对值必在两端:负数越靠左绝对值越大,非负数越靠右越大。取两端绝对值较大者 value,计入一种新的平方结果。

随后同时从左右两端跳过所有绝对值等于 value 的元素。重复值以及一正一负的对应值都在这一轮消耗完,因此下轮不会再次计数。每次至少消耗一个元素,直到区间为空。

取绝对值前先提升到 64 位,保证 32 位最小负数也能正确处理。空数组不进入循环,返回 0;两侧跳过时都检查指针是否交错,避免同一位置处理后越界。

解题步骤

  1. 比较两端元素的绝对值,确定当前最大的平方等价类并计数一次。
  2. 从左右两端同时跳过所有该绝对值的元素。
  3. 重复直到两指针交错,返回不同平方值数量。

代码实现

class Solution {
    public int distinctSquares(int[] a) {
        int left = 0;
        int right = a.length - 1;
        int count = 0;

        while (left <= right) {
            long value = Math.max(Math.abs((long) a[left]), Math.abs((long) a[right]));

            count++;

            while (left <= right && Math.abs((long) a[left]) == value) {
                left++;
            }

            while (left <= right && Math.abs((long) a[right]) == value) {
                right--;
            }
        }

        return count;
    }
}
func distinctSquares(a []int) int {
    abs := func(x int) int64 {
        v := int64(x)
        if v < 0 {
            return -v
        }
        return v
    }
    left, right, count := 0, len(a)-1, 0
    for left <= right {
        value := max(abs(a[left]), abs(a[right]))
        count++
        for left <= right && abs(a[left]) == value {
            left++
        }
        for left <= right && abs(a[right]) == value {
            right--
        }
    }
    return count
}

复杂度分析

  • 时间复杂度:$O(n)$。
  • 空间复杂度:额外空间 $O(1)$。

关键点总结

[!green]

平方是否相同只取决于绝对值,不需要做乘法;相同绝对值的正负两侧必须在同一轮去重。

易错点总结

[!yellow]

先转64位再取绝对值,避免32位最小负数溢出;两侧都要去重。

相似题目

题目 难度 关联与区别
977. 有序数组的平方 简单 同样从两端选择最大绝对值,本题只数不同结果,不实际计算或保存平方数组。
26. 删除有序数组中的重复项 简单 复用有序数据相邻重复项整段跳过的思路,这里还要合并正负相同绝对值。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/81885935
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!