面试题 - 让两个字符串相加的函数

657 阅读4分钟

面试官:写一个函数,这个函数接收两个超长字符串,字符串都是有数字组成的,函数返回这两个字符串相加后的结果。

我: 这个很简单啊,只需要将两个字符串转换成数字类型,返回他们相加的结果就好了。于是我写下了以下的程序。

    function sumup(s1, s2) {
        return Number(s1) + Number(s2)
    }

面试官:不不不,这个程序不对,我说的是超长字符串,无法转换为数字类型进行存储的,比如几百位的那种。

现在我明白了面试官想要问的问题,于是我陷入了思考。

整理思路,写出代码

这个问题其实并不算很难,所以我很快就想出了一个思路:既然这个字符串无法转换为数字类型进行运算,那我们可以像手动算加法的时候,从最后一位开始,再解决进位的问题,问题就解决了。

第一步:因为是从最后一位开始进行运算的,所以我首先想到的就是一个for的倒叙循环,这个循环的开始项,是从比较长的那个字符串的最后一位的下标开始的。

    function sumup(s1, s2) {
        let len = s1.length;
        if(len < s2.length) len = s2.length
        for(let i = len; i >= 0; i--) {
            // 后续逻辑
        }
    }

第二步:接下来我们就要每一位的运算了,之前提到了进位的问题,那么顺便就把这个也解决了吧。首先根据下标取出字符串的最后一位,因为字符串有一个短的,所以在根据取值的时候也可能取不到值(undefined),所以在取值的时候做一个非真值判断就可以了,如果值为假的,则返回0。

进位的实现:可以事先准备一个变量,默认值为0,在我们每次进行每位的运算时,将相加结果大于10的部分,存到这个变量中,在下一位运算的时候加上这个值就可以了,思路可以,接下来上代码。

    function sumup(s1, s2) {
        let len = s1.length;
        if(len < s2.length) len = s2.length;
        let res = "";
        let ten = 0;
        for(let i = len; i >= 0; i--) {
            // 后续逻辑
            const e1 = s1[i] || 0
            const e2 = s2[i] || 0
            // 这里因为 ten 的默认值是0,所以第一次循环就可以加上这个变量,0嘛,加不加不影响结果的
            const result = e1 + e2 + ten;
            // 这里res为啥不用+=呢?
            // 因为我们是从最后一位开始运算的,所以新运算出来的值应当拼接在前面
            res = result % 10 + res;
            ten = Math.floor(result / 10)
        }
        return res;
    }

第三步:检查自己的代码。这一步也是很重要,写完代码之后需要回想自己的代码逻辑,有没有遗漏的地方。上面的这段代码就有一个逻辑问题,最后一次循环算出来的进位,并没有拼接到结果前面,所以还需要一些改动

    function sumup(s1, s2) {
        let len = s1.length;
        if(len < s2.length) len = s2.length;
        let res = "";
        let ten = 0;
        for(let i = len; i >= 0; i--) {
            // 后续逻辑
            const e1 = s1[i] || 0
            const e2 = s2[i] || 0
            // 这里因为 ten 的默认值是0,所以第一次循环就可以加上这个变量,0嘛,加不加不影响结果的
            const result = Number(e1) + Number(e2) + ten;
            // 这里res为啥不用+=呢?
            // 因为我们是从最后一位开始运算的,所以新运算出来的值应当拼接在前面
            res = result % 10 + res;
            ten = Math.floor(result / 10)
        }
        
        // 补充的部分
        if(ten != 0) res = ten + res;
        
        return res;
    }

到了这里,我面试时写的代码就已经写好了,于是我就提交了。function("111111", "222") 输出: 333111; 。。。 是的,输出的不是 111333,程序的运行出错了,但是我却不知道错在哪了。面试官说已经理解我的思路,程序的bug私下去调。

解决问题

本着对技术钻研的态度,我也不可能放弃这道题目,所以在面试结束后,我就去研究代码的bug了。

终于最终是被我找到了问题,程序中在进行for循环的时候,下标是较长字符串最后一位的下标,但是这个下标却不是较短字符串最后一位的下标,所以在取值的时候,取到的较短字符串的值就不是它的最后一位了。

问题的关键已经找到了,接下来就是解决了,也就是需要保证在循环的时候,取到的位数,都是按照最后一位往前推移的。所以采用从0开始正循环,在取最后一位的时候,下标通过字符串的总长度减去循环的(i+1)来获得。

    // 最终代码
    function sumUp(s1, s2) {
        const lenS1 = s1.length
        const lenS2 = s2.length
        let len = lenS1;
        if (len < lenS2) len = lenS2
        let res = ''
        let ten = 0

        for (let i = 0; i < len; i++) {
            const e1 = s1[lenS1 - i - 1] || '0'
            const e2 = s2[lenS2 - i - 1] || '0'
            const result = Number(e1) + Number(e2) + ten
            ten = Math.floor(result / 10)
            res = result % 10 + res
        }
        if (ten != 0) res = ten + res;
        return res
    }

问题思考

上面的代码已经解决了面试官的问题,但是我在想,如果传入的字符串是小数形式呢,各位看官有没有思路,欢迎留言。