1.# 大厂高频算法atoi
前段时间和几个开水团的老同事约着搓了一顿自助火锅,味道还不错,可以无限干肉、还能自助奶茶xxx…略过?
他们有的去了中小公司当leader,也有好几个去了字节,明确字节必考算法,而且不是说要求你能做多难的题目,而是介意你有没有刷过算法…没刷过基本很难通过,其中有一道中等难度算法题字符串转换整数 (atoi)被问到好多次,来瞅瞅
2.# 字符串转换整数 (atoi)
2.1# 题目很长,我们一起耐心看完噢
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
- 如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
- 返回整数作为最终结果。
注意:
- 本题中的空白字符只包括空格字符 ’ ’ 。
- 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
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.# 题意解析
这道题乍一看比较唬人,题干这么长,首先会刷掉一波没耐心读完的火冒三丈老哥。咳,大可不必这样,控制住你的情绪,想想是不是这样:题目越长,给的细节越多,提供的信息越多 甚至有可能解法都在题里了
-
读入字符串并丢弃无用的前导空格。
条件一是在告诉我们要先去除前置空格
-
检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。:
条件2在暗示我们要注意开头的"+"和"-"
-
读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
条件3在提示我们遇到了 非数字就结束解析
-
将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
条件4在告诉我们要注意 去除首部0
-
如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
条件5太明显了,直接告诉我们整数的范围
-
返回整数作为最终结果。
条件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
好,惊呆我了
最后
希望能一直给大家分享实用、基础、进阶的知识点,一起早早下班,快乐摸鱼。