题目描述

✅ 633. 平方数之和

image-20260928235859830

题意分析

判断非负整数 c 是否能表示为两个整数的平方和。负数与其相反数的平方相同,所以只需找非负整数;交换两数也不影响结果,可以约定较小者在前。两数允许相等,也允许为 0。

解法:双指针

核心思路

[!blue]

平方项非负,因此任一候选的平方都不能超过 c,两数只能位于 0 到 floor(sqrt(c)) 之间。非负数的平方随数值增大而增大,可以把这些平方看成一个无需实际构造的有序数组,用左右指针寻找目标和。

令 left = 0、right = floor(sqrt(c))。若 left² + right² < c,当前 left 即使配上剩余最大的 right 也不够,配更小的数只会更小,因此可以排除这个 left,令它加一。若平方和 > c,当前 right 即使配上剩余最小的 left 也过大,配更大的数仍会过大,因此排除这个 right,令它减一。

每次移动都排除一个不可能参与答案的端点,仍有解时不会越过它。平方和相等就找到答案;left > right 表示所有有序候选都已排除。两端相等仍是一种合法配对,所以循环条件必须保留等号。

输入虽在 32 位有符号整数范围内,枚举到的两个平方之和仍可能超过该范围。代码从端点开始使用 Java 的 long 或 Go 的 int64,让乘法和加法都在宽整数中完成。

解题步骤

  1. 将左端设为 0,右端设为 sqrt(c) 向下取整。
  2. 在 left <= right 时计算两端平方和,相等则返回 true。
  3. 平方和较小时只增加左端,较大时只减少右端。
  4. 两端交错后返回 false。c = 0 时两端均为 0,第一轮就能确认答案;完全平方数也可以由左端的 0 配合右端得到。

代码实现

class Solution {
    public boolean judgeSquareSum(int c) {
        long left = 0;
        long right = (long) Math.sqrt(c);

        // 允许相同两数,端点重合仍要检查
        while (left <= right) {
            long sum = left * left + right * right;

            if (sum == c) {
                return true;
            } else if (sum < c) {
                // 当前左值配任何更小右值都不足,排除左值
                left++;
            } else {
                // 当前右值配任何更大左值都超标,排除右值
                right--;
            }
        }

        return false;
    }
}
import "math"

func judgeSquareSum(c int) bool {
    left := int64(0)
    right := int64(math.Sqrt(float64(c)))
    target := int64(c)

    // 允许相同两数,端点重合仍要检查
    for left <= right {
        sum := left*left + right*right
        if sum == target {
            return true
        } else if sum < target {
            // 当前左值配任何更小右值都不足,排除左值
            left++
        } else {
            // 当前右值配任何更大左值都超标,排除右值
            right--
        }
    }
    return false
}

复杂度分析

  • 时间复杂度:$O(\sqrt c+1)$。每轮至少让一个端点向内移动一步,初始区间只有 $\lfloor\sqrt c\rfloor+1$ 个候选。
  • 空间复杂度:$O(1)$,只保存两个端点与平方和。

关键点总结

[!green]

  • 候选两数允许相等,循环必须包含等号。
  • 单调性建立在非负平方候选上。

易错点总结

[!yellow]

  • 循环写成严格小于会漏掉二这样的等值配对。
  • 左端从一开始,会漏掉零平方参与的答案。
  • 先以窄整数乘法计算再转换,转换时已经可能溢出。

相似题目

题目 难度 关联与区别
69. x 的平方根 简单 整数平方根确定枚举或双指针上界,本题还要寻找另一个平方补数。
167. 两数之和 II - 输入有序数组 中等 平方数是有序序列,可把问题视为两个虚拟有序元素的目标和双指针。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2024/34310134
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!