Description
Given two binary strings, return their sum (also a binary string).
The input strings are both non-empty and contains only characters 1
or 0
.
Example 1:
1 | Input: a = "11", b = "1" |
Example 2:
1 | Input: a = "1010", b = "1011" |
分析
给定两个只包含 0 和 1 的非空字符串,求他们的二进制和。有题意得知,题目给定的字符串是合法的,可以省略输入合法性校验。
我们需要考虑的如下两个问题:
- a 和 b 的长度不一样
- 进位问题 (carry)
按如下步骤,
- 先选择字符串长度较小者为基准对字符串做反向遍历
- 每次遍历对对应字符和进位求和 ,再对和对 2 分别取余和取整,余数插入新字符串的头部,整数作为进位。
- 重复步骤 2 直到循环结束。
- 循环结束后如果进位非 0,则进位再与剩下的字符相加(如果有剩余字符)
- 返回结果
朴素解法
储备知识
String vs StringBuilder
在 Java 中,和常量一样 String 是不可变的(immutable) ,因此,对字符串做可变(insert/update/delete等)操作时,会产生新的字符串,旧的字符串会当作垃圾丢弃。频繁的字符串操作严重降低了性能,对垃圾回收造成较大压力,所以 Java 提供 StringBuilder 类型用于字符串的可变操作, StringBuilder 提供了 append、delete、insert 等接口。
StringBuilder vs StringBuffer
StringBuffer 和 StringBuilder 类似,也是用于字符串的可变操作。然而任何操作在多线程中都会存在状态同步(原子操作/一致性)问题,StringBuffer 是线程安全的,因此开销会大一些,在单线程中使用 StringBuffer 对字符串做操作是较好的选择。
StringBuilder 提供的接口
StringBuilder 含有 String 的大部分方法(是否是继承同一个父类?或实现同一个接口?没做过调查)
想要的操作 | 提供的接口 | 注解 |
---|---|---|
追加一个字符串 | append(String s) | 也可以追加 char/int/boolean等类型,功能和 + 类似 |
删除指定位置的字符 | deleteCharAt(int index) | |
指定位置插入字符 | insert(int offset, char x) | 也可以插入String/int/boolean 等 |
代码
按照如上分析,我们可以使用 StringBuilder 对字符串做操作,使用其提供的 insert 方法实现头部插入,具体如下(代码未实现完)
1 | class Solution { |
写了 80% 的时候发现,特殊逻辑太多,进来先判断 a 和 b 的长度,后续还要做一次判断,该方法太繁琐,我决定再作思考,优化特殊逻辑。
刚去了一趟厕所,想了一下可以在循环体中做特殊逻辑,如下,
1 | class Solution { |
提交后 Ac 了,内存使用打败 99.94%,时间就刚刚及格,62.66%。分析了以下,慢的原因是多次头部插入 result.insert
,像这个问题,每次遍历的时候会往头部插入,最后输出有从头开始,很典型的先进后出问题,用栈优化以下就好啦。
使用 Stack
1 | class Solution { |
本来打算使用 Stack 优化朴素算法中的频繁 Insert 问题,结果花费了 3ms,之战胜了 13% 的提交,最终参考了别人的提交,优化思路是每次都在结果中 append,最后在 reverse 字符串。
先 Append 后 Reverse
1 | class Solution { |
果然,提交后内存和时间基本都是最优状态。