我正在参加「掘金·启航计划」
题目
给定一个 正整数 num
,编写一个函数,如果 num
是一个完全平方数,则返回 true
,否则返回 false
。
进阶:不要 使用任何内置的库函数,如 sqrt
。
示例 1:
- 输入:
num = 16
- 输出:
true
示例 2:
- 输入:
num = 14
- 输出:
false
方法一:使用内置的库函数
思路及解法
根据完全平方数的性质,我们只需要直接判断 的平方根 是否为整数即可。对于不能判断浮点数的值是否等于整数的语言,则可以通过以下规则判断:
- 若 为正整数,则有 ,其中符号 表示 的向下取整。
class Solution {
func isPerfectSquare(_ num: Int) -> Bool {
let x: Int = Int(sqrt(Double(num)))
return x * x == num
}
}
复杂度分析
代码中使用的 函数的时空复杂度与 CPU
支持的指令集相关,这里不深入分析。
方法二:暴力
思路及解法
如果 为完全平方数,那么一定存在正整数 满足 。于是我们可以从 1 开始,从小到大遍历所有正整数,寻找是否存在满足 的正整数 。在遍历中,如果出现正整数 使 ,那么更大的正整数也不可能满足 ,不需要继续遍历了。
代码
class Solution {
func isPerfectSquare(_ num: Int) -> Bool {
var x: Int = 1
var square: Int = 1
while square <= num {
if square == num {
return true
}
x += 1
square = x * x
}
return false
}
}
复杂度分析
-
时间复杂度:,其中 为正整数 的最大值。我们最多需要遍历 个正整数。
-
空间复杂度:。
方法三:二分查找
思路及解法
考虑使用二分查找来优化方法二中的搜索过程。因为 是正整数,所以若正整数 满足 ,则 一定满足 。于是我们可以将 1 和 作为二分查找搜索区间的初始边界。
细节
因为我们在移动左侧边界 和右侧边界 时,新的搜索区间都不会包含被检查的下标 ,所以搜索区间的边界始终是我们没有检查过的。因此,当 时,我们仍需要检查 。
代码
class Solution {
func isPerfectSquare(_ num: Int) -> Bool {
var left: Int = 0
var right: Int = num
while left <= right {
let mid = (right - left) / 2 + left
let square = mid * mid
if square < num {
left = mid + 1
} else if square > num {
right = mid - 1
} else {
return true
}
}
return false
}
}
复杂度分析
-
时间复杂度:,其中 为正整数 的最大值。
-
空间复杂度:。