ValidPerfectSquare [source code]
public class ValidPerfectSquare {
static
/******************************************************************************/
public class Solution {
public boolean isPerfectSquare(int num) {
if (num == 0) return false;
else if (num == 1) return true;
int lo = 1, hi = num - 1;
while (lo <= hi) {
int guess = lo + (hi - lo) / 2;
double divRes = (double) num / guess;
if (divRes == guess) break;
else if (divRes < guess) hi = guess - 1;
else lo = guess + 1;
}
int res = lo + (hi - lo) / 2;
return (double) num / res == res;
}
}
/******************************************************************************/
public static void main(String[] args) {
ValidPerfectSquare.Solution tester = new ValidPerfectSquare.Solution();
int[] inputs = {1,2,3,4,5,15,16,32,64,10000};
for (int i : inputs) System.out.printf("%d -> %b%n", i, tester.isPerfectSquare(i));
}
}
这个题目总体来说还是比较简单的, 不过真正能够想到 Binary Search 也并不是那么简单. 最后的速度是0ms (40%), 估计是最优解了; 0ms (40%).
这里刚开始有一个抉择, 就是 header 用什么判断. 刚开始的想法是用类似!(num / guess == guess) || (num % guess == 0)
来完成的. 但是这个就碰到了一个类似 Binary Search 的时候的问题: 你循环内部的一个 var 用来帮助进行循环的 header 的判断. 最后认为比较好的写法就是跟 Binary Search 一样的写法, 直接用 lo 和 hi 进行判断;
这个模板也可以应用于所有其他的 Binary Search 的变形应用上面: Binary Search 最后你真正维护的就是 hi 和 lo 这两个边界, 而不是 mid 这样的中间量(虽然直接对这个 mid 进行判断来terminate 显得方便很多, 但是这样写出来的代码就丑很多);
一个小的技术细节, 关于 JAVA 里面的整数除法和浮点除法:
java> 8 == (65 / 8)
java.lang.Boolean res0 = true
java> int i = 8
int i = 8
java> double d = 65 / 8
double d = 8.0
java> double d = (65) / 8
double d = 8.0
java> double d2 = (int) 65 / 8
double d2 = 8.0
java> double d2 = (double) 65 / 8
double d2 = 8.125
java> 8 == d2
java.lang.Boolean res4 = false
java> 8 == d
java.lang.Boolean res5 = true
是否能够 promote 浮点除法完全取决于两个除数里面有没有一个是浮点数, 而不取决于你 operator 本身怎么写, 也不取决于你最后的左值是什么类型(左值是 double 并不能保证除法就是浮点除法);
discussion 的最优解有很多也是用的这个做法, 不过他们习惯于这么写:
public boolean isPerfectSquare(int num) {
if (num < 1) return false;
long left = 1, right = num;// long type to avoid 2147483647 case
while (left <= right) {
long mid = left + (right - left) / 2;
long t = mid * mid;
if (t > num) {
right = mid - 1;
} else if (t < num) {
left = mid + 1;
} else {
return true;
}
}
最后的区别在于他们是用乘法来检查, 这样就避免了检查 truncation 的问题; 注意, 这个写法要求最后 mid 用 long, 不然可能会 overflow;
这个是 discussion 上面一个有意思的代码:
public boolean isPerfectSquare(int num) {
int i = 1;
while (num > 0) {
num -= i;
i += 2;
}
return num == 0;
}
这个依赖的原理是所有的 square 都可以表示成为1 + 3 + 5 + 7 + ...; 这个算法最后的复杂度是O(sqrt(n)):
Σ(1 + 2i) = n =>
x + 2Σi = n =>
x + 2(x(x+1)) = n =>
2x^2 + 3x = n =>
x = [-3 +/- sqrt(9 + 8n)]/4 =>
you can see that n is in a square root term, so the complexity should be O(sqrt(n)).
另外可以用 Newton Method 直接计算平方根:
public boolean isPerfectSquare(int num) {
long x = num;
while (x * x > num) {
x = (x + num / x) >> 1;
}
return x * x == num;
}
Newton Method 最后计算出来的 halt 的地方是floor(sqrt(n)) , 所以最后还要专门检查一下 num 是不是perfect square;
另外可以看到:
java> 17/2
java.lang.Integer res0 = 8
java> 17>>1
java.lang.Integer res1 = 8
java> 23/4
java.lang.Integer res6 = 5
java> 23>>2
java.lang.Integer res7 = 5
shift 完成的其实是跟普通的除法一样的, 带 truncate 的除法;
Problem Description
Given a positive integer num, write a function which returns True if num is a perfect square else False.
Note: Do not use any built-in library function such as sqrt.
Example 1:
Input: 16
Returns: True
Example 2:
Input: 14
Returns: False
Credits:
Special thanks to @elmirap for adding this problem and creating all test cases.
Difficulty:Easy
Category:Algorithms
Acceptance:38.03%
Contributor: LeetCode
Companies
linkedin
Related Topics
binary search math
Similar Questions
Sqrt(x) Sum of Square Numbers