题目
完全平方数是一个可以写成某个整数的平方的整数,换句话说,它可以写成某个整数和自身的乘积。现给你一个正整数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、初始化i为1。
2、当i * i <= num时,重复以下操作。
(1)如果i * i == num,则返回True,表示num是一个完全平方数。
(2)否则,增加i的值,并继续循环。
3、如果循环结束而没有找到符合条件的i,则返回False,表示num不是一个完全平方数。
根据上面的算法步骤,我们可以得出下面的示例代码。
def is_perfect_square_by_brute_force(num):
if num < 1:
return False
i = 1
while i * i <= num:
if i * i == num:
return True
i += 1
return False
print(is_perfect_square__by_brute_force(16))
print(is_perfect_square__by_brute_force(14))
二分搜索法
二分搜索法的基本思想是:通过不断地将搜索区间对半分割来缩小搜索范围,直到找到目标值或确定目标值不存在。使用二分搜索法求解本题的主要步骤如下。
1、初始化搜索区间为[1, num]。
2、当搜索区间有效时(即左端点不大于右端点),计算区间的中间值mid,并重复进行如下操作。
(1)如果mid * mid == num,则返回True,表示num是一个完全平方数。
(2)如果mid * mid < num,则更新左端点为mid + 1。
(3)如果mid * mid > num,则更新右端点为mid - 1。
3、如果搜索结束后仍未找到符合条件的mid,则返回False,表示num不是一个完全平方数。
根据上面的算法步骤,我们可以得出下面的示例代码。
def is_perfect_square_by_binary_search(num):
if num < 1:
return False
left, right = 1, num
while left <= right:
mid = (left + right) // 2
square = mid * mid
if square == num:
return True
elif square < num:
left = mid + 1
else:
right = mid - 1
return False
print(is_perfect_square_by_binary_search(16))
print(is_perfect_square_by_binary_search(14))
牛顿迭代法
牛顿迭代法是一种用于求解方程近似根的迭代方法,对于本问题,我们可以使用牛顿迭代法来逼近num的平方根。如果最终逼近的值的平方等于num,则num是一个完全平方数。使用牛顿迭代法求解本题的主要步骤如下。
1、初始化猜测值x为num / 2或者1,这取决于num的大小。
2、使用牛顿迭代公式,更新猜测值为x = (x + num / x) / 2。
3、重复步骤2,直到x的变化非常小,或者x的平方等于num。
4、如果最终x的平方等于num,则返回True,表示num是一个完全平方数。否则,返回False。
根据上面的算法步骤,我们可以得出下面的示例代码。
def is_perfect_square_by_newton_method(num):
if num < 1:
return False
# 初始化猜测值
x = num
if num < 3:
x = 1
# 设置精度阈值
precision = 1e-10
# 牛顿迭代
while True:
new_x = (x + num / x) / 2
if abs(new_x - x) < precision or new_x * new_x == num:
break
x = new_x
return int(x) * int(x) == num
print(is_perfect_square_by_newton_method(16))
print(is_perfect_square_by_newton_method(14))
总结
使用暴力法时,我们需要从1开始逐步增加i,直到i * i > num,故其时间复杂度为O(n的平方根)。暴力法的优点是实现简单,易于理解,但效率较低,特别是对于较大的num。
使用二分搜索法时,搜索区间每次减半,因此时间复杂度为O(logn)。相对于暴力法,二分搜索法的效率更高,且实现并不复杂。
牛顿迭代法的收敛速度非常快,通常只需要几次迭代就可以达到很高的精度,其时间复杂度为O(log(log n))。牛顿迭代法的效率非常高,尤其在num较大时,表现更佳。
💡 需要《Python面试宝典》完整源码的大佬们,可订阅专栏后,搜索微信公众号“希望睿智”私信获取。