LeetCode - 633. Sum of Square Numbers(平方数之和)(数学/二分)

LeetCode - 633. Sum of Square Numbers(平方数之和)(数学/二分)

  • 方法一-数学
  • 方法二-Hash表
  • 方法三-二分

题目链接
题目

在这里插入图片描述

方法一-数学

首先想到肯定不会是双重循环的枚举,只需要枚举一个数a,去检查b是否满足即可,又因为是平方数,所以最对只需要枚举sqrt( c )即可:

class Solution {
    public boolean judgeSquareSum(int c) {
        if (c == 0)
            return true;
        for (int a = 0; a <= Math.sqrt(c); a++) {
            int b = (int) Math.sqrt(c - a * a);
            if (c == a * a + b * b)
                return true;
        }
        return false;
    }
}

或者这样,原理是一样的:

class Solution {
    public boolean judgeSquareSum(int c) {
        if (c == 0)
            return true;
        for (int a = 0; a <= Math.sqrt(c); a++) {
            int b2 = c - a * a;
            int b = (int) Math.sqrt(b2);
            if (b * b == b2)
                return true;
        }
        return false;
    }
}

这里有个问题就是,当我们把for循环中的a <= Math.sqrt( c )改一下,改成下面的代码,就会超时:

class Solution {
    public boolean judgeSquareSum(int c) {
        if (c == 0)
            return true;
        for (int a = 0; a * a <= c; a++) {
            int b2 = c - a * a;
            int b = (int) Math.sqrt(b2);
            if (b * b == b2)
                return true;
        }
        return false;
    }
}

这是为什么呢? 因为当 c接近于Math.Integer的时候,a*a有可能会发生溢出,于是枚举的时候将a改成long类型:

class Solution {
    public boolean judgeSquareSum(int c) {
        if (c == 0)
            return true;
        for (long a = 0; a * a <= c; a++) {
            long b2 = c - a * a;
            long b = (long) Math.sqrt(b2);
            if (b * b == b2)
                return true;
        }
        return false;
    }
}

方法二-Hash表

这题也可以和LeetCode第一题 Two Sum一样,使用Hash表存储答案的一边,然后遍历去寻找另一边:

class Solution {
    public boolean judgeSquareSum(int c) {
        if (c == 0)
            return true;
        HashSet<Integer> set = new HashSet<>();
        for (int a = 0; a <= Math.sqrt(c); a++)
            set.add(a * a);
        for (int b = 0; b <= Math.sqrt(c); b++) {
            if (set.contains(c - b * b))
                return true;
        }
        return false;
    }
}

同样可以从2*N优化到N,只需要遍历一遍数组 :
但是这里要注意:
set.add(c - a * a);这一句和那个第一题不同,这个要放在判断的前面,因为像1 * 1 + 1 * 1 = 2这种情况,就要这样判断。

class Solution {
    public boolean judgeSquareSum(int c) {
        if (c == 0)
            return true;
        HashSet<Integer> set = new HashSet<>();
        for (int a = 0; a <= Math.sqrt(c); a++) {
            set.add(c - a * a); //这个要放在前面,因为像 1*1 + 1*1 = 2就是这种例子
            if (set.contains(a * a))
                return true;
        }
        return false;
    }
}

方法三-二分

还有一个很巧妙的方法:

  • 因为ab都一定在[0,sqrt( c )]之间;
  • 我们可以在[0,sqrt( c )],设置两个指针L,R,每次看这两个指针对应的平方和是否= c
  • 如果等于就返回true
  • 如果 > ,说明右边值要缩小才有可能= ,所以R--
  • 如果 < ,说明左边值要增加才有可能= ,所以L++
class Solution {
    public boolean judgeSquareSum(int c) {
        int L = 0, R = (int)Math.sqrt(c);
        while(L <= R){
            int cur = L*L + R*R;
            if(cur == c)
                return true;
            else if(cur > c)
                R--;
            else 
                L++;
        }
        return false;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值