js_algorithm icon indicating copy to clipboard operation
js_algorithm copied to clipboard

字符串转换整数 (atoi)

Open Cosen95 opened this issue 4 years ago • 1 comments

leetcode: https://leetcode-cn.com/problems/string-to-integer-atoi/

Cosen95 avatar May 06 '20 13:05 Cosen95

题目分析

做这道题目大致分为4步

Step1: 计算区间

对应题目描述:

假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231,  231 − 1]。如果数值超过这个范围,请返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。

用代码描述就是:

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

Step2: 解析字符串

我们看到这道题,首先可能会想到去遍历字符串,然后依次判断,但仔细想一想,对于字符串的处理用正则来做是不是更好呢?

我这里先把正则给出:

/\s*([-\+]?[0-9]*).*/

下面来分析下:

  • 首先,\s*,这里的意思就是空格出现0次或多次,都可被匹配到。(对应题目描述该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止)
  • 接着是一个(),对应正则中就是捕获组,在这道题里,我们需要从字符串中提取的其实只有+/-符号以及其后面的数字而已,同时这个字符串需要满足 可能存在的空格+正负号+数字字符串+其它字符内容 这样的格式才算合法,那我们就可以通过这样写正则表达式,实现“匹配”和“提取”的双重目的
  • 其次,()中的内容为[-\+]?[0-9]*,对应分别是用于匹配-+和0个或多个0-9 之间的整数
  • 最后的 .*用于字符串尾部匹配非数字的任意字符。我们看到.*是被排除捕获组之外的,所以说这个东西其实也不会被额外存储,它被“摘除”了。

Step3:获取捕获结果

JS 的正则相关方法中, test()方法返回的是一个布尔值,单纯判断“是否匹配”。要想获取匹配的结果,我们需要调度match()方法:

const reg = /\s*([-\+]?[0-9]*).*/
const groups = str.match(reg)

match() 方法是一个在字符串中执行查找匹配的String方法,它返回一个数组,在未匹配到时会返回 null

这里match()会返回第一个完整匹配(作为数组的第0项)及其相关的捕获组(作为数组的第1及第1+项)。

这里我们只定义了一个捕获组,因此可以从 groups[1] 里拿到我们捕获的结果。

判断区间

最后一步,就是把捕获的结果转换成数字,看看是否超出了题目要求的范围。

编码实现

// 入参是一个字符串
const myAtoi = function(str) {
    // 编写正则表达式
    const reg = /\s*([-\+]?[0-9]*).*/
    // 得到捕获组
    const groups = str.match(reg)
    // 计算最大值
    const max = Math.pow(2,31) - 1
    // 计算最小值
    const min = -max - 1
    // targetNum 用于存储转化出来的数字
    let targetNum = 0
    // 如果匹配成功
    if(groups) {
        // 尝试转化捕获到的结构
        targetNum = +groups[1]
        // 注意,即便成功,也可能出现非数字的情况,比如单一个'+'
        if(isNaN(targetNum)) {
            // 不能进行有效的转换时,请返回 0
            targetNum = 0
        }
    }
    // 卡口判断
    if(targetNum > max) {
        return max
    } else if( targetNum < min) {
        return min
    }
    // 返回转换结果
    return targetNum
};

Cosen95 avatar May 06 '20 13:05 Cosen95