每日一例 | 从算法角度看代码的优化:整数反转

云中志

共 6835字,需浏览 14分钟

 ·

2021-05-14 15:38

题目来源:力扣(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(01));
            } 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 -


浏览 7
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报