1.# 大厂高频算法atoi

前段时间和几个开水团的老同事约着搓了一顿自助火锅,味道还不错,可以无限干肉、还能自助奶茶xxx…略过?

他们有的去了中小公司当leader,也有好几个去了字节,明确字节必考算法,而且不是说要求你能做多难的题目,而是介意你有没有刷过算法…没刷过基本很难通过,其中有一道中等难度算法题字符串转换整数 (atoi)被问到好多次,来瞅瞅

2.# 字符串转换整数 (atoi)

2.1# 题目很长,我们一起耐心看完噢

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

  1. 读入字符串并丢弃无用的前导空格
  2. 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
  3. 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  4. 将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
  5. 如果整数数超过 32 位有符号整数范围 [−231,  231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
  6. 返回整数作为最终结果。

注意:

  • 本题中的空白字符只包括空格字符 ’ ’ 。
  • 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

2.2# 示例也很长,很快就看完了啦

示例 1:

输入:s = "42" 输出:42 解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。 第 1 步:"42"(当前没有读入字符,因为没有前导空格) ^ 第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^ 第 3 步:"42"(读入 "42") ^ 解析得到整数 42 。 由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42

示例 2:

输入:s = " -42" 输出:-42 解释: 第 1 步:" -42"(读入前导空格,但忽视掉) ^ 第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数) ^ 第 3 步:" -42"(读入 "42") ^ 解析得到整数 -42 。 由于 "-42" 在范围 [-231, 231 - 1] 内,最终结果为 -42

示例 3:

输入:s = "4193 with words" 输出:4193 解释: 第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格) ^ 第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+') ^ 第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止) ^ 解析得到整数 4193 。 由于 "4193" 在范围 [-231, 231 - 1] 内,最终结果为 4193

3.# 我怀疑你在考验我的耐心

终于看完题了,不知道各位心里是什么感受,一万只”草泥马“从草原奔腾而过?是的我也是,因为题目和示例太他么的长了,长到我压根不想看…

我觉得他们压根就是不想招人… 整这么长的题目

4.# 题意解析

这道题乍一看比较唬人,题干这么长,首先会刷掉一波没耐心读完的火冒三丈老哥。咳,大可不必这样,控制住你的情绪,想想是不是这样:题目越长,给的细节越多,提供的信息越多 甚至有可能解法都在题里了

  1. 读入字符串并丢弃无用的前导空格。条件一是在告诉我们要先去除前置空格

  2. 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。:条件2在暗示我们要注意开头的"+"和"-"

  3. 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。条件3在提示我们遇到了 非数字就结束解析

  4. 将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。条件4在告诉我们要注意 去除首部0

  5. 如果整数数超过 32 位有符号整数范围 [−231,  231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。条件5太明显了,直接告诉我们整数的范围

  6. 返回整数作为最终结果。条件6在暗示我们一定是整数,不能是小数噢

5.# parseInt霸气解法

看了这一大坨的题目和题意解析,我怀疑面试官不仅仅考验我的耐心还想让我实现一个parseInt,但我偏偏不想遂了你的意,我就要用parseInt来做

/** * @param {string} s * @return {number} */ const myAtoi = function(s) { let result = parseInt(s) const max = Math.pow(2, 31) - 1 const min = -max - 1 // parseInt('fatfish') => NaN // 范围限定 return isNaN(result) ? 0 : (result > max ? max : (result < min ? min : result)) }

你别说,还真过了所有的用例,而且成绩还很好,只是我估计面试官脸都绿了,直接补了一刀,不允许使用parseInt

6.# 正则解析法

没办法,被限制了系统API的调用,只能自己写个解析器了,这道题当看到要去除空格,限制数字相信你很快也想到了用正则将会特别方便

6.1# step1: 去除空格

这个很简单,一个^\s*就可以搞定

6.2# step2: 符号确认

也很简单,[\+-]?只可能是+或者-,甚至有可能没有符号位

6.3 step3: 数字解析

这部分是最重要的,将数字部分摘出来\d*,哈哈,是不是搞笑,这么容易?

6.4 step4: 去除首部多余的0

一个+号就搞定了,来看看示例

+'0' => 0 +'00123' => 123

6.5 step5: 范围限定

// 计算最大值 const max = Math.pow(2,31) - 1 // 计算最小值 const min = -max - 1

6.6 step6: 整数返回

这个我们放在整体解析中看

6.7 正则解法

/** * @param {string} s * @return {number} */ const myAtoi = function(s) { /* 1. ^\s* 匹配可能存在的空格 step1 2. ([\+-]?\d*) 匹配符号和数字 step2和step3 3. \D*非数字解析 */ const match = s.match(/^\s*([\+-]?\d*)\D*/) // step5 范围限定 const max = Math.pow(2, 31) - 1 const min = -max - 1 let result if (match) { // step4 去除首部多余的0 result = +match[1] // step6 整数返回 result = isNaN(result) ? 0 : (result > max ? max : (result < min ? min : result)) } return result };

结果居然比直接调用parseInt好,惊呆我了

最后

希望能一直给大家分享实用、基础、进阶的知识点,一起早早下班,快乐摸鱼。