每日leetcode

367. 有效的完全平方数 - 力扣(LeetCode)

题目

给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。

不能使用任何内置的库函数,如  sqrt 。

示例 1:

输入:num = 16
输出:true
解释:返回 true ,因为 4 * 4 = 16 且 4 是一个整数。

示例 2:

输入:num = 14
输出:false
解释:返回 false ,因为 3.742 * 3.742 = 14 但 3.742 不是一个整数。

提示:

  • 1 <= num <= 231 - 1

思路

  1. 也是二分查找方法,上下界为[1,num],不过不能用因子乘的方法,会出现整型越界,而是要用除法检查是否是因子。即商如果等于除数且能整除(因为int有去尾),那么就返回true,如果大于除数,则说明除数小了,缩小左边界,其余缩小右边界。

代码实现

class Solution {
public:
    bool isPerfectSquare(int num) {
        int left = 1, right = num;
        while(left <= right) {
            int mid = left + (right-left) / 2;
            int result = num / mid;
            if(result==mid && num%mid==0) return true;
            else if(result > mid) left = mid + 1;
            else right = mid - 1;
        }
        return false;
    }
};

复杂度分析

  • 时间复杂度:O(logn)。
  • 空间复杂度:O(1)。

官方题解(除了牛顿迭代法还有个梯度下降法,有空复习一下,这里mark一下)

  • 官解罗列了一个时空间复杂度和二分查找基本一致的方法——牛顿迭代法,这是一种通过直线方程得到曲线方程近似解的方法。
  • 首先确认我们想要求解的问题是x*x=num,那么他们差多少其实可以通过f(x)y=x*x-num来表达,然后我们每次从一个合理的初始值开始计算,根据y的情况就可以对x进行更新从而逼近y=0的点,这里初始值不妨选择我们二分选择的端点,不过因为这条方程的零点是-根号(num)和根号(num),显然我们需要求的是后者,为了希望移动的方向大体上是一个方向,所以不妨就取边上的num,那么第一步的方向肯定就是缩小了。
  • 然后根据直线斜率k=(y-y0)/(x-x0),目前我们已经初始值(x0,y0),我们可以求得这一点上的曲线的切线的斜率方程,切线和x轴交点的方向就是x0的前进方向,又k(x0)=f'(x0)=2*x0,所以我们直接代入k然后令y=0就可以得到(2*x0)*(x-x0)+(x0*x0-num)=0,所以x=(-x0^2+num+2x0^2)/(2x0)=0.5*(x0+num/x0),这就是下一个新的x0。
  • 什么时候结束呢?因为我们的计算是通过double来计算的,所以计算的结束标志就是两者的差值足够小即可(不过不知道为什么这里官解没有算绝对值来判断,如果跑过头了咋办,有没有懂哥解释一下?),那么就可以近似看成同一值了,最后再转成整型看看平方后是否等于num即可。
  • 复现:
  • class Solution {
    public:
        bool isPerfectSquare(int num) {
            double x0 = num;
            while(1) {
                double x1 = (x0 + num/x0) / 2;
                if(abs(x0-x1) < 1e-6) break;
                x0 = x1;
            }
            int x = (int) x0;
            return x * x == num;
        }
    };
  • 显然这个方法的空间复杂度是O(1)的,那么时间复杂度怎么计算呢(我也不会)?学吧,学就完事儿了。
  • 感觉题解的复杂度分析并不严谨,上网查了查假设我们设计的停步阈值为e=1e-6,那么时间复杂度就为O(1/e^2),在强光滑凸条件下复杂度为O(log(1/e)),所以这个方法的使用也是需要考虑前提的(不过如果不是这样的话二分查找也不能用了就是了)。

你可能感兴趣的:(leetcode训练,leetcode,算法,职场和发展,c++,二分查找)