更多精彩文章请关注公众号:TanLiuYi00

题目

解题思路

题目要求非负整数 x 的平方根,相当于求函数 y = √x 中 y 的值。

函数 y = √x  图像如下:

从上图中,可以看出函数是单调递增的,满足二分查找的条件(区间是有序的),所以可以用二分查找去做。

解题步骤

  • 比较 mid * mid 跟 x 的大小,相等则直接返回 mid,否则就去以 mid 为分割点的左右区间查找,直到不满足循环循环条件(left == right + 1 或 left == right 究竟是哪一个主要跟循环条件有关,请看后面的分析)就退出。
  • 由于非负整数 x(当 x ≠ 0 时) 的平方根一定是落在区间 [1, x/2 + 1],所以左右边界分别取 1 和 x/2 + 1,而不分别取 0 和 x,这样可缩小查找范围。
  • 为了防止 mid * mid 太大而发生整型溢出,取 mid 跟 x/mid 比较。

说明

右边界 right 取 x/2 + 1,而不取 x/2,这是因为当 x = 1 时,right 如果取 x/2,由于 x/2 会向下取整使得 x/2 = 0,此时 left = 1 大于 right = 0,以至于直接跳出循环,导致 1 的平方根为 0,这明显是错误的。

Show me the Code

int mySqrt(int x){
int left = 1, right = x / 2 + 1;
// 循环不变量 始终维持在区间 [left, right] 中查找,当 left = right + 1 时,区间为空,查找结束
// 当 left == right 时,区间 [left, right] 依然有效
while (left <= right) {
// 防止溢出
int mid = left + ((right - left) >> 1);
// mid 大于 √x ,在 mid 前半区间 [left, mid - 1] 中查找,不是 [left, mid]
// 是因为会当查找到 target 时,直接返回 mid,所以没必要再考虑 mid
if (mid > x / mid) {
right = mid - 1;
// mid 小于 √x ,在 mid 后半区间 [mid + 1, right] 中查找
} else if (mid < x / mid) {
left = mid + 1;
// mid 等于 √x ,代表查找到 target,则直接返回
} else {
return mid;
}
} return right;
}

补充说明

如果没有出现 mid == x / mid 的情况,最后到底是 return right 还是 return left?
1、可以通过调试得出;

2、循环结束的条件是 left = right + 1,示例中提示了当 x = 8,其平方根是 2,有点类似于向下取整的意思,所以是 return right 。

进一步补充

可能有些童鞋会问到,上面代码中循环的条件为何是 left <= right 而不是 left < right,其实这两个条件都可以,主要区别在于:

循环结束的条件不一样,前者是 left = right + 1 后者是 left = right,即前者当 left = right + 1 时,查找区间 [left, right] 为空,后者当 left = right 时,查找区间 [left, right) 为空。

定义的右边界取值不一样,前者右边界取值为 x / 2 + 1,后者可为 x / 2 + 2。

这里会提到一个循环不变量的概念:

循环不变量

1. 初始化:它在循环的第一轮迭代开始之前,应该是正确的。

2. 如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。

3. 当循环结束时,不变式给了我们一个有用的性质,它有助于表明算法是正确的。

所以本题循环的条件可以是 left <= right 也可以是 left < right,关键是查找的过程中需要一直维护查找区间 [left, right] 或 [left, right)。

下面补充一个循环条件为 left < right 的代码。

最后返回的是 right - 1,这是因为:

定义的查找区间是左闭右开[left, right),取不到右边界 right;当 left == right 时,循环退出,由于 right 取不到并且平方根有点向下取整的意味,所以取 right - 1;

通过调试也能得出。

Show me the Code

int mySqrt(int x){
int left = 1, right = x / 2 + 2;
// 循环不变量 始终维持在区间 [left, right) 中查找,当 left = right 时,区间为空,查找结束
while (left < right) {
// 防止溢出
int mid = left + ((right - left) >> 1);
// mid 大于 √x ,在 mid 前半区间 [left, mid) 中查找
if (mid > x / mid) {
right = mid;
// mid 小于 √x ,在 mid 后半区间 [mid + 1, right) 中查找
} else if (mid < x / mid) {
left = mid + 1;
// mid 等于 √x ,代表查找到 target,则直接返回
} else {
return mid;
}
}
return right - 1;
}

更多精彩文章可关注公众号:TanLiuYi00

  

  

字节笔试题 leetcode 69. x 的平方根的更多相关文章

  1. Java实现 LeetCode 69 x的平方根

    69. x 的平方根 实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去. 示例 1: 输入: ...

  2. [leetcode] 69. x 的平方根(纯int溢出判断实现)

    69. x 的平方根 非常简单的一个题,用二分法逼近求出ans即可,额外注意下溢出问题. 不过我要给自己增加难度,用long或者BigNum实现没意思,只能使用int类型 换句话当出现溢出时我们自己得 ...

  3. LeetCode 69 x 的平方根

    链接:https://leetcode-cn.com/problems/sqrtx 实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数, ...

  4. [LeetCode]69. x 的平方根(数学,二分)

    题目 https://leetcode-cn.com/problems/sqrtx 题解 方法一:牛顿迭代法 按点斜式求出直线方程(即过点Xn,f(Xn)),然后求出直线与x轴交点,即为Xn+1: 求 ...

  5. C/C++ 笔试题

    /////转自http://blog.csdn.net/suxinpingtao51/article/details/8015147#userconsent# 微软亚洲技术中心的面试题!!! 1.进程 ...

  6. 嵌入式Linux C笔试题积累(转)

    http://blog.csdn.net/h_armony/article/details/6764811 1.   嵌入式系统中断服务子程序(ISR) 中断是嵌入式系统中重要的组成部分,这导致了很 ...

  7. C/C++笔试题(很多)

    微软亚洲技术中心的面试题!!! .进程和线程的差别. 线程是指进程内的一个执行单元,也是进程内的可调度实体. 与进程的区别: (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位 (2 ...

  8. 非常全面的java基础笔试题

    下面是java基础笔试题,当时我去笔试,做了1个小时(80道选择题,后面的简答题就没时间做了),结果很吓人,太挫了,最后被面试官忽悠去培训去了,呵呵.我偷偷把面试题弄了下来,用来学习吧,也希望能对你们 ...

  9. C/C++笔试题(基础题)

    为了便于温故而知新,特于此整理 C/C++ 方面相关面试题.分享,共勉. (备注:各题的重要程度与先后顺序无关.不断更新中......欢迎补充) (1)分析下面程序的输出(* 与 -- 运算符优先级问 ...

随机推荐

  1. 计算机网络安全 —— 报文摘要算法 ME5 (三)

    一.报文摘要算法基本概念 使用加密通常可达到报文鉴别的目的,因为伪造的报文解密后一般不能得到可理解的内容.但简单采用这种方法,计算机很难自动识别报文是否被篡改.另外,对于不需要保密而只需要报文鉴别的网 ...

  2. 如何创建一个验证请求的API框架

    ​开发一款成功软件的关键是良好的架构设计.优秀的设计不仅允许开发人员轻松地编写新功能,而且还能丝滑的适应各种变化. 好的设计应该关注应用程序的核心,即领域. 不幸的是,这很容易将领域与不属于这一层的职 ...

  3. Docker的Ubuntu镜像安装的容器无ifconfig命令和ping命令

    就这三步骤,下面的是实例不看也罢. apt-get update ###第一步一定要先执行这个更新下.不更新下面的安装命令会显示找不到网络包 //ifconfig apt install net-to ...

  4. https://nginx.org/en/docs/http/request_processing.html

    https://nginx.org/en/docs/http/request_processing.html

  5. 初始TypeScript

    什么是TypeScript? TypeScript是拥有类型系统的JavaScript的超集,可以编译成纯JavaScript: 1.类型检查:TS会在编译代码时进行严格的静态类型检查,这意味着可以在 ...

  6. C++ Primer Plus读书笔记(三)复合类型

    这节主要是介绍数组.结构体.类等概念,以及new delete内存管理. 1.数组 首先普及一下C++中字符串的概念,只有在结尾有 \0 的才叫字符串, cout 输出字符串也以空字符为标记进行结束输 ...

  7. 洛谷P3833

    Description 树链剖分板子题 考查两种操作 A u v w 把 u 节点到 v 节点路径上所有节点权值加 w Q u 求以 u 为根节点的子树权值之和 首先需要了解线段树和 dfs 序,我这 ...

  8. 洛谷P3850 书架

    题目描述 Knuth先生家里有个精致的书架,书架上有N本书,如今他想学到更多的知识,于是又买来了M本不同的新书.现在他要把新买的书依次插入到书架中,他已经把每本书要插入的位置标记好了,并且相应的将它们 ...

  9. Mysql数据库用户及用户权限管理,Navicat设置用户权限

    Mysql数据库用户及用户权限管理,Navicat设置用户权限 一.Mysql数据库的权限 1.1 mysql数据库用户权限级别 1.2 mysql数据库用户权限 1.3 存放用户权限表的说明 二.用 ...

  10. Spring MVC参数处理

    使用Servlet API作为参数 HttpServletRequest HttpServletResponse HttpSession 使用流作为参数 总结 Spring MVC通过分析处理处理方法 ...