背景
某次面试,上来直接自我介绍,完了马上写题,出的一个大数相加的题目:
即不使用语言的高阶函数的前提下,两个字符串形式的远大于int和long类型的整数相加,要求输出对应的结果
用例:“11111111111111111111111”,“11111111111111111111111”
输出:“22222222222222222222222”
思路
当即得到这个题目直接心凉了一半,完蛋,没刷过的题怎么写?
第一反应是写个循环无限减去一个long的最大值,直到这个值不越界为止,然后直接一加,但是这个好像把输入转换成对应的值就溢出了,我倒是想知道什么高阶函数能处理这个事情,问题脑子里也不知道啥高阶函数什么BigNumber,BigInteger之类的?问题是根据这个题目意思也肯定不是让你转什么类型去做啊。
脑子一边想一边先写个输入判断吧,找不到正确答案先排除错误答案呗,先判断这两个字符串是纯粹的数值相加,不然乱七八糟的字符串进来这逻辑也处理不了,写一个工具方法判断字符串是不是纯数值,这个简单,遍历字符串,如果没有落在'0'和'9'的区间内就有问题,这个很好得到:
判断完怎么办呢?类似思路硬算呗,整个数会溢出,那我遍历字符串,一位一位总不会溢出了吧,撑死了就是你俩数值加起来18,我就进位,一位一位进,进到最后总能得出结果吧,ok思路就这么突然出来了。
根据朴素的小学加数理解,竖式计算,那我就从个位数开始对齐一直加呗,每一位加完拼出一个字符串,最后把结果反转一下完事!
思路就这么整了,写了个大概,还没开始完善,面试官说20分钟到了,做完了吗?可以大概口述一下思路么?当时大概是这么个代码
思路就是:
1.先判断数据格式,确认没问题
2.判断两个字符串哪边比较长,以长的那个字符串为基准,从后面位数逐位相加,如果有大于等于10的情况需要记录这个进位的情况,每一位的值拼接,如果前面相加有进位,那这一位拼接的时候就要+1,以此类推拼完之后再把字符串反转
3.记录进位情况的话使用一个Map来做记录,因为使用的时候要判断上一位,所以循环开始前先把最先的那一位进位情况记录为false
4.长段对短段的遍历结束后再把剩余部分都加上(还没来得及写)
说完面试官说没问题的,大致思路就是这样,细节可能还有一些内容,时间有限就不问了,就先这样吧,我们再聊一点其他问题
后来
面试完,复盘的时候想,写都写了,完善一下跑点case看看,上来直接一堆错误,重新审视了一下代码发现还是挺稀碎的,问题多多
1.判断输入字符串格式时,没有进行最基础的判空和字符串长度为0
解决方法:这个简单,直接进行isEmpty的判断或者自己进行判空和字符串长度为0的判断
2.位次对齐,纯粹以长的字符串的i为基准索引,直接会导致短的那边边界溢出,程序直接崩
解决方法:需要两个索引,以各自的长度减一开始,在短字符串的遍历到索引为0之前自减,进行相加,记录时以长字符串的索引为是否进位的key进行标识,因为短字符串遍历完之后是长字符串的+0,此时需要用的也是长字符串的进位判断
这下总大功告成了吧,写完之后打印了几个case,"1111111","11111",长的短的,换下顺序,欸都没错,再来点空字符串,带符号的字符串,ok都没问题,美滋滋
如果没有意外的话,意外马上就发生了:
"99111111", "1111111"
我靠直接抛出异常了怎么回事?添加日志输出中间结果,打印,找不到问题。只好使出断点大法,woc懂了
3.没有特别注意的长的部分和虚空的0相加,也会有相加大于10的情况!!
试想一下,长段的相加就不用考虑大于等于10的情况了吗?好像确实不用啊,本身的数加虚空,顶多加一个前面长段和短段的相加进位判断,那就判断一下呗,ok,长段的遍历也把进位判断一下
但是这完了吗?也没有,极端情况下就是这个问题case,由前面相加得出的进位一路判断,最后会导致最后的结果一路以一个10的效果推进,所以不仅我们需要按照上面的逻辑做同样的判断,甚至,最后还要给最后一位也加上判断,不然你可以最后相加得到10,然后反转后得到一个0开头的结果字符串
解决方法:给这个相加的部分加上上面类似的判断,最后给最后一位补上对应的逻辑,即根据进位情况补充一个1
于是乎我们的核心循环逻辑变成了这样:
把s2>s1同样的逻辑补充完了以后对应跑的case效果:
这样下来一个大数相加,逐位处理的逻辑就基本完成了
进一步
从上面逻辑来看整个没有很难的东西,但是确实需要考虑相对较多的复杂情况,边界的case还是需要处理的,避免落入一下常规的思维陷阱。
再者,这里是整数的相加,那么如果这个大数还有带小数点的情况,这个逻辑是否会写呢?按图索骥其实也可以处理出来,对应的逻辑也需要再修剪修剪就是了