问题简介
编程思路
- 在遍历字符串的过程中填充符号信息、数据字符串信息。
- 根据符号和最后的数字串信息强转为有效的数字。
- 为防止越界,采用long类型的数据来存储和表示直接的数据。
- 有效性查询。
编程实现
编程
第一个版本
public int myAtoi(String s) {
char[] chars = s.toCharArray();
int sign = 1;
StringBuilder numberBuilder = new StringBuilder();
int i = 0;
while (i < chars.length && chars[i] == ' ') {
i++;
}
s = s.substring(i);
if (s.charAt(0) == '-') {
sign = -1;
s = s.substring(1);
} else if (s.charAt(0) == '+') {
sign = 1;
s = s.substring(1);
}
i = 0;
while (i < s.length() && Character.isDigit(s.charAt(i))) {
numberBuilder.append(s.charAt(i));
i++;
}
String numberStr = numberBuilder.toString();
if (numberStr.isEmpty()) {
return 0;
}
long value = Long.valueOf(numberStr) * sign;
if (value < Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
}
if (value > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
return (int) value;
}
AC版本
public int myAtoi(String s) {
char[] chars = s.toCharArray();
int sign = 1;
StringBuilder numberBuilder = new StringBuilder();
int i = 0;
while (i < chars.length && chars[i] == ' ') {
i++;
}
s = s.substring(i);
if (s.isEmpty()) {
return 0;
}
if (s.charAt(0) == '-') {
sign = -1;
s = s.substring(1);
} else if (s.charAt(0) == '+') {
sign = 1;
s = s.substring(1);
}
i = 0;
while (i < s.length() && Character.isDigit(s.charAt(i))) {
numberBuilder.append(s.charAt(i));
i++;
}
String numberStr = numberBuilder.toString();
if (numberStr.isEmpty()) {
return 0;
}
i = 0;
while (i < numberStr.length() && numberStr.charAt(i) == '0') {
i++;
}
numberStr = numberStr.substring(i);
if (numberStr.isEmpty()) {
return 0;
}
if (numberStr.compareTo(String.valueOf(Long.MAX_VALUE)) > 0 || numberStr.length()>=20) {
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
long value = Long.valueOf(numberStr) * sign;
if (value < Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
}
if (value > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
return (int) value;
}
AC版本之前缀提炼函数-不追求效率使用现有函数
public int myAtoi(String s) {
int sign = 1;
StringBuilder numberBuilder = new StringBuilder();
int i = 0;
s = removeStartPrefix(s, " ");
s = s.substring(i);
if (s.isEmpty()) {
return 0;
}
if (s.charAt(0) == '-') {
sign = -1;
s = s.substring(1);
} else if (s.charAt(0) == '+') {
sign = 1;
s = s.substring(1);
}
i = 0;
while (i < s.length() && Character.isDigit(s.charAt(i))) {
numberBuilder.append(s.charAt(i));
i++;
}
String numberStr = numberBuilder.toString();
if (numberStr.isEmpty()) {
return 0;
}
String prefix = "0";
numberStr = removeStartPrefix(numberStr, prefix);
if (numberStr.isEmpty()) {
return 0;
}
if (numberStr.compareTo(String.valueOf(Long.MAX_VALUE)) > 0 || numberStr.length()>=20) {
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
long value = Long.valueOf(numberStr) * sign;
if (value < Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
}
if (value > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
return (int) value;
}
private String removeStartPrefix(String numberStr, String prefix) {
while (numberStr.startsWith(prefix)) {
numberStr = numberStr.substring(1);
}
return numberStr;
}
遇到的问题
越界问题
越界问题是由于在获取所有的数字数据到容器numberBuilder中,未执行i++,导致内存溢出。
然后是未包含对于长度的校验导致越界,出现问题的代码片段如下所示:
i = 0;
while (i < s.length() && Character.isDigit(s.charAt(i))) {
numberBuilder.append(s.charAt(i));
i++;
}
String numberStr = numberBuilder.toString();
程序无法处理边界条件空串
未考虑空串,**从空串中取charAt(0)**抛出了异常
这是由于
public int myAtoi(String s) {
char[] chars = s.toCharArray();
int sign = 1;
StringBuilder numberBuilder = new StringBuilder();
int i = 0;
while (i < chars.length && chars[i] == ' ') {
i++;
}
s = s.substring(i);
if (s.isEmpty()) {
return 0;
}
// 如果传入的字符串s为空串,则0号位是无法获取的,s.charAt(0)抛出了异常所导致,所以在此之前添加了对于s如果为空直接返回0.
if (s.charAt(0) == '-') {
sign = -1;
s = s.substring(1);
} else if (s.charAt(0) == '+') {
sign = 1;
s = s.substring(1);
}
无法处理**“20000000000000000000”**
在使用Long.parseLong的时,判断数字是否越界,根据Long最长19位进行判断。
if (numberStr.length()>=20) {
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
无法处理"00000-42a1234"
在移除无效的前导零之后,可能会返回“”导致问题。
因此在移除前导0之后,对空串进行判断。
i = 0;
while (i < s.length() && Character.isDigit(s.charAt(i))) {
numberBuilder.append(s.charAt(i));
i++;
}
String numberStr = numberBuilder.toString();
if (numberStr.isEmpty()) {
return 0;
}
i = 0;
while (i < chars.length && chars[i] == '0') {
i++;
}
numberStr = numberStr.substring(i);
if (numberStr.isEmpty()) {
return 0;
}
if (numberStr.length()>=20) {
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
无法处理"-000000000000000000000000000000000000000000000000001"
无法处理**“20000000000000000000”**
之前的处理该数字的过程仍然无法有效,最终修改程序如下:
i = 0;
while (i < numberStr.length() && numberStr.charAt(i) == '0') {
i++;
}
numberStr = numberStr.substring(i);
if (numberStr.isEmpty()) {
return 0;
}
if (numberStr.compareTo(String.valueOf(Long.MAX_VALUE)) > 0 || numberStr.length()>=20) {
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
long value = Long.valueOf(numberStr) * sign;
if (value < Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
}
if (value > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
return (int) value;
numberStr.compareTo(String.valueOf(Long.MAX_VALUE)) > 0 || numberStr.length()>=20
该表达式通过比较字符串在比较了长度>=20的时候,按照符号返回Integer的最大和最小值。但在比较19位与Long.MAX_VALUE的最大值采用了字符串的比较方式,才最终完成了比较。
总结
这个题目基本问题的答出还是比较简单的,程序的基本框架搭建成功也不复杂,但是,在处理越界问题、前导0问题时真的踩了不少坑。在处理前导0问题时可以方便使用如下的方式进行:
while(str.startWith('0')) {
str = str.substring(1);
}
这样比较简单,不要考虑效率,起初,尤其是编程题目的时候,正确性大于效率。
我们把上述的手法提炼成函数,并内联,之后形成的代码如下:参见AC版本之前缀提炼函数-不追求效率使用现有函数
从上面可以看出内存消耗多用了0.1M,无所谓的,正确性远大于效率。调试的时间占用非常大,所以先梳理好程序的基本过程,理解输入输出,和处理逻辑非常的重要。
这个程序最重要的收获就是比较Long的最大值的问题了。
不特么纠结了,好累的,眼睛也很痛。明天去南阳了。希望有个好心情。