【经典算法】LeetCode 69. x 的平方根(Java/C/Python3/Golang实现含注释说明,Easy)

简介: 【经典算法】LeetCode 69. x 的平方根(Java/C/Python3/Golang实现含注释说明,Easy)

x 的平方根

  • 标签(题目类型):数学、二分查找

题目描述

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4
输出: 2

示例 2:

输入: 8
输出: 2
解释: 8 的平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。

原题:LeetCode 69. x 的平方根

思路及实现

方式一:二分查找

思路

由于平方根函数的性质,我们知道平方根一定位于0和x之间(x为非负整数)。因此,我们可以使用二分查找算法在0到x之间查找平方根。在每次迭代中,我们计算中间值mid的平方,如果它等于x,则mid就是平方根;如果它小于x,则平方根一定在mid的右侧;如果它大于x,则平方根一定在mid的左侧。通过不断缩小查找范围,最终我们可以找到平方根。

代码实现

Java版本
public class Solution {
    public int mySqrt(int x) {
        if (x < 2) return x; // 特殊情况处理
        long left = 2; // 左边界设为2,因为1的平方根为1,无需查找
        long right = x / 2; // 右边界设为x/2,因为平方根不会大于x/2
        while (left <= right) {
            long mid = left + (right - left) / 2; // 防止溢出
            long square = mid * mid;
            if (square == x) {
                return (int) mid;
            } else if (square < x) {
                left = mid + 1; // 平方根在mid右侧
            } else {
                right = mid - 1; // 平方根在mid左侧
            }
        }
        // 因为我们查找的是小于等于x的最大的平方根,所以返回right
        return (int) right;
    }
}

说明:

  • 使用long类型进行中间计算,防止整数溢出。
  • 当square等于x时直接返回结果。
  • 如果square小于x,则平方根一定在mid的右侧,因此更新左边界为mid + 1。
  • 如果square大于x,则平方根一定在mid的左侧,因此更新右边界为mid - 1。
  • 最终返回的是right,因为在循环结束时,right会指向小于等于x的最大的平方根。
C语言版本
#include <math.h>
int mySqrt(int x) {
    if (x < 2) return x;
    long left = 2;
    long right = x / 2;
    while (left <= right) {
        long mid = left + (right - left) / 2;
        long square = mid * mid;
        if (square == x) {
            return (int)mid;
        } else if (square < x) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return (int)right;
}

说明:

  • 与Java版本类似,C语言版本也使用了二分查找的思想。
  • 使用了long类型进行中间计算以避免溢出。
Python3版本
class Solution:
    def mySqrt(self, x: int) -> int:
        if x < 2: return x
        left = 2
        right = x // 2
        while left <= right:
            mid = (left + right) // 2
            square = mid * mid
            if square == x:
                return mid
            elif square < x:
                left = mid + 1
            else:
                right = mid - 1
        return right

说明:

  • Python版本同样采用了二分查找的思路。
  • 由于Python整数运算不会溢出,这里不需要使用额外的数据类型来避免溢出。
Golang版本
package main
import (
  "math"
)
func mySqrt(x int) int {
  if x < 2 {
    return x
  }
  left := 2
  right := x / 2
  for left <= right {
    mid := left + (right-left)/2
    square := mid * mid
    if square == x {
      return mid
    } else if square < x {
      left = mid + 1
    } else {
      right = mid - 1
    }
  }
  return right
}
func main() {
  // 测试代码
  x := 8
  result := mySqrt(x)
  println(result) // 输出应为 2
}

说明:

  • Golang版本同样使用了二分查找的思想来计算平方根。
  • left和right分别表示查找范围的左右边界。
  • 在循环中,根据中间值mid的平方与x的比较结果来更新查找范围。
  • 最终返回right,它是小于等于x的最大的平方根。

复杂度分析

  • 时间复杂度:O(log x),因为每次迭代都将查找范围减半。
  • 空间复杂度:O(1),只使用了常数级别的额外空间。

方式二:牛顿迭代法

思路

牛顿迭代法是一种在实数上近似求解方程的方法。对于平方根的计算,我们可以使用牛顿迭代法的公式, 来不断逼近平方根的值。

牛顿迭代法的公式可以表示为:

xₙ₊₁ = xₙ - f(xₙ) / f’(xₙ)

其中,xₙ是第n次迭代的解, xₙ₊₁是第n+1次迭代的解, f(xₙ)是函数在xₙ处的函数值, f’(xₙ)是函数在xₙ处的导数值。

这个公式用于不断逼近函数的根,直到满足一定的精度要求。

代码实现

Java版本
public class Solution {
    public int mySqrt(int x) {
        if (x == 0) return 0;
        double last = 0, curr = x;
        while (Math.abs(curr - last) > 0.00001) {
            last = curr;
            curr = (curr + x / curr) / 2;
        }
        return (int) curr;
    }
}

说明:

  • 初始化last为0,curr为x。
  • 在循环中,根据牛顿迭代法的公式更新curr的值。
  • 当curr和last的差值小于某个很小的阈值时,认为找到了足够接近的解,跳出循环。
  • 返回curr的整数部分作为结果。
C语言版本
#include <math.h>
int mySqrt(int x) {
    if (x == 0) return 0;
    double last = 0, curr = x;
    double epsilon = 0.00001;
    while (fabs(curr - last) > epsilon) {
        last = curr;
        curr = (curr + x / curr) / 2;
    }
    return (int)curr;
}

说明:

  • C语言版本与Java版本类似,使用了牛顿迭代法来逼近平方根。
  • 使用了fabs函数来计算浮点数之间的绝对值。
Python3版本
class Solution:
    def mySqrt(self, x: int) -> int:
        if x == 0: return 0
        last = 0.0
        curr = x
        epsilon = 0.00001
        while abs(curr - last) > epsilon:
            last = curr
            curr = (curr + x / curr) / 2
        return int(curr)

说明:

  • Python版本同样使用了牛顿迭代法。
  • 使用了abs函数来计算浮点数之间的绝对值。
Golang版本
package main
import (
  "math"
)
func mySqrt(x int) int {
  if x == 0 {
    return 0
  }
  last := 0.0
  curr := float64(x)
  epsilon := 0.00001
  for math.Abs(curr-last) > epsilon {
    last = curr
    curr = (curr + float64(x)/curr) / 2
  }
  return int(curr)
}
func main() {
  // 测试代码
  x := 8
  result := mySqrt(x)
  println(result) // 输出应为 2
}

说明:

  • Golang版本使用了牛顿迭代法来计算平方根。
  • epsilon定义了收敛的阈值,当连续两次迭代结果的差值小于这个阈值时,认为找到了足够精确的解。
  • math.Abs函数用于计算浮点数之间的绝对值。

复杂度分析

  • 时间复杂度:与选择的阈值epsilon有关,但通常很快收敛,所以时间复杂度相对较低。
  • 空间复杂度:O(1),只使用了常数级别的额外空间。

总结

方法 优点 缺点 时间复杂度 空间复杂度 其他
二分查找 思路简单,直观易懂 可能不是最优解,对于非整数平方根需要额外处理 O(log x) O(1) 适用于整数平方根计算
牛顿迭代 收敛速度快,通常很快能得到近似解 需要选择合适的初始值和阈值 近似O(1) O(1) 适用于需要高精度或浮点数平方根计算

相似题目

相似题目 难度 链接
平方根的四舍五入 中等 力扣-6905
求一个数的立方根 中等 力扣-69
计算整数除法 简单 力扣-7
计算平方和 简单 力扣-665
最近的平方数 简单 力扣-676

这些题目都涉及到数学运算和数值计算,与平方根计算有一定的相似性,可以用于加深对数值计算和相关算法的理解。请注意,这里提供的链接是基于假设的,实际链接需要根据具体的在线编程平台(如力扣)进行查找。

相关文章
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
人工智能 算法 NoSQL
LRU算法的Java实现
LRU(Least Recently Used)算法用于淘汰最近最少使用的数据,常应用于内存管理策略中。在Redis中,通过`maxmemory-policy`配置实现不同淘汰策略,如`allkeys-lru`和`volatile-lru`等,采用采样方式近似LRU以优化性能。Java中可通过`LinkedHashMap`轻松实现LRUCache,利用其`accessOrder`特性和`removeEldestEntry`方法完成缓存淘汰逻辑,代码简洁高效。
664 0
|
存储 算法 安全
探究‘公司禁用 U 盘’背后的哈希表算法与 Java 实现
在数字化办公时代,信息安全至关重要。许多公司采取“禁用U盘”策略,利用哈希表算法高效管理外接设备的接入权限。哈希表通过哈希函数将设备标识映射到数组索引,快速判断U盘是否授权。例如,公司预先将允许的U盘标识存入哈希表,新设备接入时迅速验证,未授权则禁止传输并报警。这有效防止恶意软件和数据泄露,保障企业信息安全。 代码示例展示了如何用Java实现简单的哈希表,模拟公司U盘管控场景。哈希表不仅用于设备管理,还在文件索引、用户权限等多方面助力信息安全防线的构建,为企业数字化进程保驾护航。
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
1497 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
12月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
存储 Linux iOS开发
Python入门:2.注释与变量的全面解析
在学习Python编程的过程中,注释和变量是必须掌握的两个基础概念。注释帮助我们理解代码的意图,而变量则是用于存储和操作数据的核心工具。熟练掌握这两者,不仅能提高代码的可读性和维护性,还能为后续学习复杂编程概念打下坚实的基础。
Python入门:2.注释与变量的全面解析
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
2803 2
|
存储 算法 Java
LeetCode经典算法题:打家劫舍java详解
LeetCode经典算法题:打家劫舍java详解
376 2
|
搜索推荐 算法 Java
经典排序算法之-----选择排序(Java实现)
这篇文章通过Java代码示例详细解释了选择排序算法的实现过程,包括算法的基本思想、核心代码、辅助函数以及测试结果,展示了如何通过选择排序对数组进行升序排列。
经典排序算法之-----选择排序(Java实现)