有效的完全平方数(力扣第367题)
题目:
给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。
说明:不要使用任何内置的库函数,如 sqrt。
分析:
这个题是一个简单题,通过循环能够很容易的做出来,但是如果只用简单的循环去判断是否是完全平方数会超出时间限制,也就是说这样的方式性能太低了。所以只能通过其他方式让程序的运行更为快捷。
方法一:暴力循环,lue
public boolean isPerfectSquare(int num) { if (num == 1){ return true; } int flag = 0; int i = 2; while (i <= (num/2)){ if (i * i == num){ flag = 1; }else if (i * i > num){ break; } i++; } if (flag == 1){ return true; }else { return false; } }
方法二:等差序列
分析一下,完全平方数:0,1,4,9,16,25,36,…… 它们之间的差值依次是1,3,5,7,9,11;这是一个等差数列,用一个表格更加清晰的表示一下它们递进的过程:
平方数 | 差值 |
0 | 1 |
1 | 3 |
4 | 5 |
9 | 7 |
16 | 9 |
25 | 11 |
…… |
可以看出,从第一个完全平方数0开始,它加上第一个差值1,就是下一个完全平方数,依次类推,这些完全平方数之间的差值是一个递增的等差数列,而且会发现第i个完全平方数,其实就是前i-1个差值相加之后的结果,比如第3个完全平方数是4,那么前两个差值相加1+3就等于4。
根据这个规律我们可以让num依次减去差值,最后判断num是否等于0,等于0说明是完全平方数,不等于0,说明不是。差值初始值是1,每次递增2;
public boolean isPerfectSquare4(int num) { if (num < 2){ return true; } int sumnum = 1; while (num > 0){ num -= sumnum; sumnum += 2; } return (num == 0); }
方法三:二分查找
分析可以,一个大于2的完全平方数,其因子一定小于等于他的一半,那我们就设置两个指针l和r,分别指向第一个数和num/2,然后判断mid = (l + r) /2 的平方是否和num相等,如果不等那就再具体分析,大于num就减少右指针,r = mid - 1,小于num就增加左指针,l = mid + 1,遍历的整个过程如果没有mid平方值和num相等的情况,那就说明不是完全平方。
public boolean isPerfectSquare3(int num) { if (num == 1){ return true; } long r = num /2; long l = 2; while (l <= r){ long mid = (r - l) /2 + l; if (mid * mid == num){ return true; }else if (mid * mid > num){ r = mid - 1; }else { l = mid + 1; } } return false; }
方法四:牛顿迭代法
设置一个函数f(x) = x2-num,当f(x) = x2-num = 0时,x这个根如何求?牛顿迭代法,就是不断用这个二次函数的切线,不断去逼近二次函数和x轴的交点。具体做法就是,先寻找一点xk ,求出对应的函数值f(xk),然后求出在这一点处的二次函数的切线,这个是很好求,已经知道一个点(xk,f(xk)),然后对二次函数求导,再将xk这个值带入可以求出切线斜率,就可以求出这个切线的函数表达式了,然后令这个函数表达式等于0,求出切线和x轴交点,判断是否等于二次函数和x轴交点,如果不等,就继续上述操作,不断逼近。
利用这一原理,我们就可以完成对一个数是否是完全平方数进行判断。设近似值为x,初始值设置为num/2,循环判断x*x是否大于num,如果是那就不断使用牛顿迭代法缩小近似值。具体的代码实现如下:
public boolean isPerfectSquare2(int num) { if (num == 1){ return true; } long x = num / 2; while (x * x > num){ x = (x + num / x)/2; } return (x * x == num); }
参考自:
cyc2018;