一、题目描述
Given a non-negative integer c, your task is to decide
whether there're two integers a and b such that a^2 + b^2 = c.
Example 1:
Input: 5
Output: True
Explanation: 11 + 22 = 5
Example 2:
Input: 3
Output: False
二、题解
(1) 暴力枚举(超时)
/**
* 超时:输入1000000000
* @param c
* @return
*/
public boolean judgeSquareSum1(int c) {
int n = (c >>> 1) + 1;
for (int i = 0; i <= n; i++)
for (int j = i; j <= n; j++)
if(i*i + j*j == c)
return true;
return false;
}
(2) 开平方计算
/**
* 将两次循环变利用算数运算变为一次循环
* 执行用时 8 ms 击败了 12.25% 的java用户
* 内存消耗 33MB 击败了 81% 的java用户
*/
public boolean judgeSquareSum2(int c) {
double sqrtC = Math.sqrt(c);
for(int a = 0; a <= sqrtC; a++) {
// 为什么这样减?如果c减去正在枚举的a的平方后开方,得到的仍然是一个整数,那么c一定是个平方数
double b = Math.sqrt(c - a*a);
if(b == (int)b) return true;
}
return false;
}
开平方的算法还可以写成这样
/*
* 6ms 击败30.88%
*/
public boolean judgeSquareSum2(int c) {
for(long a = 0; a*a <= c ; a++) {
double b = Math.sqrt(c - a*a);
if(b == (int)b) return true;
}
return false;
}
为什么要用long
?如果用int
,在输入2147483646
后会超时
- 分析:当
a
最后等于2147483646
这个数时,a++
会变成负数,然后又进入了循环,最终无限循环。
(3) 双指针
既然第一种做法从一段开始超时,不如我们从数字c
的两端向中间逼近,这样可以减少计算的规模。
/**
* 执行用时 3 ms 击败了 75% 的java用户
* 内存消耗 32.8MB 击败了 82.5% 的java用户
* @param c
* @return
*/
public boolean judgeSquareSum3(int c) {
int l=0, r=(int)Math.sqrt(c);
while(l <= r) {
int sum = l*l + r*r;
if(sum > c) r--;
else if(sum < c) l++;
else return true;
}
return false;
}
while 循环的条件为什么使用<=
?
- 因为当
c==2
时,l=0,r==1
,等下一次循环,l=1,r=1
,如果不是<=
,则会退出循环,返回false
,不符合题意。