前端刷题路-Day42: 平方数之和(题号633)

1,105 阅读1分钟

这是我参与更文挑战的第6天,活动详情查看: 更文挑战

平方数之和(题号633)

题目

给定一个非负整数 c ,你要判断是否存在两个整数 ab,使得 a2 + b2 = c

示例 1:

输入:c = 5
输出:true
解释:1 * 1 + 2 * 2 = 5

示例 2:

输入:c = 3
输出:false

示例 3:

输入:c = 4
输出:true

示例 4:

输入:c = 2
输出:true

示例 5:

输入:c = 1
输出:true

提示:

  • 0 <= c <= 231 - 1

链接

leetcode-cn.com/problems/su…

解释

这题啊,这题一看就是双指针。

因为没有什么更好的办法来确定ab的值,只能一点点找,那找的话就是双指针了。

左边界很好确定——0,右边界也不难,只要取数字的平方根即可(取整)。

剩下的就是双指针的经典操作,这里不多赘述。

自己的答案(双指针)

var judgeSquareSum = function(c) {
  var left = 0
      right = ~~Math.sqrt(c)
  while (left <= right) {
    var res = Math.pow(left, 2) + Math.pow(right, 2)
    if (res === c) return true
    if (res < c) {
      left++
    } else {
      right--
    }
  }
  return false
};

对吧,一看就知道答案了,没啥可说的,看了看题解发现有些人在纠结双指针会不会错过正确答案,笔者倒是没怎么纠结,不知道为啥。

让笔者比较在意的是另外一种方法性能问题👇:

另外的方法(循环)

先看看代码👇:

var judgeSquareSum = function(c) {
  for (let a = 0; a * a <= c; a++) {
    const b = Math.sqrt(c - a * a);
    if (b === parseInt(b)) {
      return true;
    }
  }
  return false;
};

讲真,乍一看这段代码笔者真没法有啥问题,甚至感觉比双指针性能更好一些。但笔者错了,不管是执行时间还是内存占用都比双指针夸张很多,这是为什么呢?

首先看看执行次数,笔者在whilefor循环的内部都进行了次数统计,发现while内部执行了17次,for循环只有16次,甚至略胜一筹,可到底是什么让它们的差距那么大呢,其实仔细一对比就能发现了。

首先,大概去掉一些类似的计算,相差一次的循环可以抹掉了,因为其实两者的内部逻辑都比较简单。

接下来看a * aMath.pow()的代码,二者都差不多,并且在循环过程中都执行了两次,这里也抹掉。

再往下看就是一些if...else和对变量的赋值操作,由于差距不大,这里也抹掉了,那最后剩下的就是Math.sqrt(),这也就是关键所在。

在双指针中,只在函数运行之初取了c的平方根,在while内部其实并没有,但在循环中,每一次循环都要调用了一次Math.sqrt(),这就是根源所在了。

Math.sqrt()多执行了15次。

更好的方法(数学)

好了,到这里就超纲了,完全不知道在说什么东西,答案说是首先要进行质因数分解,再判断所有形如 4k + 34*k*+3 的质因子的幂是否均为偶数。

贴一下代码:

var judgeSquareSum = function(c) {
  for (let base = 2; base * base <= c; base++) {
    // 如果不是因子,枚举下一个
    if (c % base !== 0) {
      continue;
    }
    // 计算 base 的幂
    let exp = 0;
    while (c % base == 0) {
      c /= base;
      exp++;
    }
    // 根据 Sum of two squares theorem 验证
    if (base % 4 === 3 && exp % 2 !== 0) {
      return false;
    }
  }
  // 例如 11 这样的用例,由于上面的 for 循环里 base * base <= c ,base == 11 的时候不会进入循环体
  // 因此在退出循环以后需要再做一次判断
  return c % 4 !== 3;
};

想具体了解的可以看看官方解答,在这里,官方甚至还“细心”地贴了证明过程了地址。。。



PS:想查看往期文章和题目可以点击下面的链接:

这里是按照日期分类的👇

前端刷题路-目录(日期分类)

经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的👇

前端刷题路-目录(题型分类)

有兴趣的也可以看看我的个人主页👇

Here is RZ