每日一例 | 从算法角度看代码的优化:整数反转
题目来源:力扣(LeetCode
)
链接:https://leetcode-cn.com/problems/reverse-integer
难度:简单
题目描述
给你一个 32
位的有符号整数 x
,返回将 x
中的数字部分反转后的结果。
如果反转后整数超过 32
位的有符号整数的范围 [−231, 231 − 1]
,就返回 0
。
假设环境不允许存储 64
位整数(有符号或无符号)。
示例 1:
输入:x = 123
输出:321
示例 2:
输入:x = -123
输出:-321
示例 3:
输入:x = 120
输出:21
示例 4:
输入:x = 0
输出:0
提示:
-2^31 <= x <= 2^31 - 1
解题过程
第一次提交
拿到这个题我的第一反应是将数值转成字符串,然后反向输出字符串,然后再将字符串转为整数,当然会判断整数是否包含符号,所以我的第一次提交也是这样的思路:
class Solution {
public int reverse(int x) {
if (x == 0) {
return 0;
}
String valueOf = String.valueOf(x);
char[] chars;
StringBuilder numberBuilder;
if (valueOf.startsWith("-") || valueOf.startsWith("+")) {
chars = valueOf.substring(1).toCharArray();
numberBuilder = new StringBuilder(valueOf.substring(0, 1));
} else {
chars = valueOf.toCharArray();
numberBuilder = new StringBuilder();
}
for (int i = chars.length - 1; i >= 0; i --) {
numberBuilder.append(chars[i]);
}
int result = 0;
try {
result = Integer.parseInt(numberBuilder.toString());
} catch (NumberFormatException e) {
return result;
}
return result;
}
}
虽然所有测试用例都通过了,但是代码很长,而且性能也不好:
第二次提交
然后我考虑换种解法,先想到的是递归,但是由于要用过程值,所以递归是行不通的,这里走了好多弯路。但你如果只是像反向打印输出一个整数的话,是可以用递归算法的:
public static void printOut(int n) {
if (n >= 10) {
printOut(n / 10);
}
System.out.println(n % 10);
}
比如输出12333
,打印结果:
接着,我反向推到了下过程,理清了思路:上次余数乘以10
,加上本次余数,但是每一次的被除数都是上次的商,比如211
,反转后是112
,第一次的余数是1
,商是21
,下一次的被除数是21
余数是是1
,商是2
,再下一次被除数是2
,余数是2
,商是0
,这时候就结束了。将上面的流程整理成代码是这样的:
class Solution {
public static int reverse(int i) {
long result = 0;
while ( i != 0) {
int yu = i % 10;
result = result * 10 + yu;
i = i / 10;
}
return (int)result == result ? (int)result : 0;
}
}
代码更简洁了,而且性能也比上面更好:
只是逻辑上更复杂更抽象了,但只要你理清了思路,上面的代码你也可以写出来。
代码优化
你觉得上面的代码还有优化的空间吗?其实还可以优化成这样:
class Solution {
public static int reverse(int i) {
long result = 0;
while ( i != 0) {
result = result * 10 + i % 10;
i = i / 10;
}
return (int)result == result ? (int)result : 0;
}
}
虽然,相比第二次提交其实就只删除了一个变量,但代码的内存消耗方面的性能表现更好了:
优化前,内存消耗是35.5 MB
,优化之后35.3MB
,第一次提交的执行用时: 3 ms
,内存消耗: 35.8 MB
,从这一点上看,代码的优化是非常必须的,毕竟系统崩溃的时候没有一行代码是无辜的
总结
算法算是一个特别特别基础,但是又特别考验编程实力的技能,随着这项技能的不断提升和精进,你不仅可以写出更高效,更健壮的系统,同时你考虑问题的角度也会比别人更专业,更有深度,而这些是与系统、与语言无关的;从更实际的角度来说,当你算法能力够强的时候,你在写业务代码的时候也会尽可能减少不必要对象的的创建、冗余代码,因为你知道每一个不必要的操作,对系统而言都是不必要内存的消耗,都为系统的崩溃埋下了雷,今天的算法优化就很好地体现了这一点。
我知道,坚持学习本身很难,偶尔(就刚刚)我也想放纵一下,想躺着的玩手机,想啥都不干,但是当我硬着头皮站在新的起跑线上的时候,当我开始做这件事的时候,我觉得我又可以了,我觉得坚持过当下就好……
而且当我静下心来的时候,我觉得娱乐和放纵本身并不能给我满足,当一切的想法都得到满足的时候,反而会让我感受到更大失落、空洞……
现在的我,坚持的意义就是靠输出倒逼自己输入,如果非要找一个坚持的理由,那就是我不满意自己当下的生活,我想做自己觉得对的事,我希望通过这些事让自己的生活更充实,更有意义,未来某一天,我希望自己回首这段时光的时候,心怀感激,满心欢喜。
当然,有时候我也会很困惑,很迷茫,不知道应该分享些什么,不知道应该如何学习,感觉自己似乎进入到了一个封闭的空间,不知道该往哪里去,也不知道该如何摆脱这一切?
但当每一个阳光明媚的早上来临的时候,一切又可以重新开始了,一切都充满了希望。在我的认知里,我觉得自己是一个积极乐观的人,我相信明天,我相信未来,我总相信事情都会越来越好,也想想越努力越幸运,所以我不想给自己的人生设限,不想给自己贴标签,但我会坚持自己的选择,按照自己的节奏用心生活,我特别喜欢罗曼罗兰的一句话——有一种英雄主义就是认清了生活的真相,却依然热爱它。
现在我把这句话也送给你,也许生活很残酷,超出了你的认知极限,黑暗、肮脏、毁三观,但依然有人在用心生活,深处泥淖,依然有人在仰望星空;也许生活很美好,充满了欢声笑语,让你感受到满心欢喜,但依然有人选择放弃生命,放弃希望。倒不是说你必须成为什么样的人,做什么事,只是希望你知道自己在做什么,自己想做什么,此时此刻你是否对当下的生活满意,毕竟生活的点点滴滴都是选择,遵循内心最真实的感受,最真实的想法,或坚持,或放弃,只要你感觉舒适就好,只要你觉得值得就好,至于其他人说什么倒是无足轻重的了……
- END -