每日一例 | 從算法角度看代碼的優(yōu)化:整數(shù)反轉

題目來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/reverse-integer
難度:簡單
題目描述
給你一個 32 位的有符號整數(shù) x ,返回將 x 中的數(shù)字部分反轉后的結果。
如果反轉后整數(shù)超過 32位的有符號整數(shù)的范圍 [?231, 231 ? 1] ,就返回 0。
假設環(huán)境不允許存儲 64位整數(shù)(有符號或無符號)。
示例 1:
輸入:x = 123
輸出:321
示例 2:
輸入:x = -123
輸出:-321
示例 3:
輸入:x = 120
輸出:21
示例 4:
輸入:x = 0
輸出:0
提示:
-2^31 <= x <= 2^31 - 1
解題過程
第一次提交
拿到這個題我的第一反應是將數(shù)值轉成字符串,然后反向輸出字符串,然后再將字符串轉為整數(shù),當然會判斷整數(shù)是否包含符號,所以我的第一次提交也是這樣的思路:
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;
}
}
雖然所有測試用例都通過了,但是代碼很長,而且性能也不好:


第二次提交
然后我考慮換種解法,先想到的是遞歸,但是由于要用過程值,所以遞歸是行不通的,這里走了好多彎路。但你如果只是像反向打印輸出一個整數(shù)的話,是可以用遞歸算法的:
public static void printOut(int n) {
if (n >= 10) {
printOut(n / 10);
}
System.out.println(n % 10);
}
比如輸出12333,打印結果:

接著,我反向推到了下過程,理清了思路:上次余數(shù)乘以10,加上本次余數(shù),但是每一次的被除數(shù)都是上次的商,比如211,反轉后是112,第一次的余數(shù)是1,商是21,下一次的被除數(shù)是21余數(shù)是是1,商是2,再下一次被除數(shù)是2,余數(shù)是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;
}
}
代碼更簡潔了,而且性能也比上面更好:


只是邏輯上更復雜更抽象了,但只要你理清了思路,上面的代碼你也可以寫出來。
代碼優(yōu)化
你覺得上面的代碼還有優(yōu)化的空間嗎?其實還可以優(yōu)化成這樣:
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;
}
}
雖然,相比第二次提交其實就只刪除了一個變量,但代碼的內(nèi)存消耗方面的性能表現(xiàn)更好了:

優(yōu)化前,內(nèi)存消耗是35.5 MB,優(yōu)化之后35.3MB,第一次提交的執(zhí)行用時: 3 ms,內(nèi)存消耗: 35.8 MB,從這一點上看,代碼的優(yōu)化是非常必須的,畢竟系統(tǒng)崩潰的時候沒有一行代碼是無辜的
總結
算法算是一個特別特別基礎,但是又特別考驗編程實力的技能,隨著這項技能的不斷提升和精進,你不僅可以寫出更高效,更健壯的系統(tǒng),同時你考慮問題的角度也會比別人更專業(yè),更有深度,而這些是與系統(tǒng)、與語言無關的;從更實際的角度來說,當你算法能力夠強的時候,你在寫業(yè)務代碼的時候也會盡可能減少不必要對象的的創(chuàng)建、冗余代碼,因為你知道每一個不必要的操作,對系統(tǒng)而言都是不必要內(nèi)存的消耗,都為系統(tǒng)的崩潰埋下了雷,今天的算法優(yōu)化就很好地體現(xiàn)了這一點。
我知道,堅持學習本身很難,偶爾(就剛剛)我也想放縱一下,想躺著的玩手機,想啥都不干,但是當我硬著頭皮站在新的起跑線上的時候,當我開始做這件事的時候,我覺得我又可以了,我覺得堅持過當下就好……
而且當我靜下心來的時候,我覺得娛樂和放縱本身并不能給我滿足,當一切的想法都得到滿足的時候,反而會讓我感受到更大失落、空洞……
現(xiàn)在的我,堅持的意義就是靠輸出倒逼自己輸入,如果非要找一個堅持的理由,那就是我不滿意自己當下的生活,我想做自己覺得對的事,我希望通過這些事讓自己的生活更充實,更有意義,未來某一天,我希望自己回首這段時光的時候,心懷感激,滿心歡喜。
當然,有時候我也會很困惑,很迷茫,不知道應該分享些什么,不知道應該如何學習,感覺自己似乎進入到了一個封閉的空間,不知道該往哪里去,也不知道該如何擺脫這一切?
但當每一個陽光明媚的早上來臨的時候,一切又可以重新開始了,一切都充滿了希望。在我的認知里,我覺得自己是一個積極樂觀的人,我相信明天,我相信未來,我總相信事情都會越來越好,也想想越努力越幸運,所以我不想給自己的人生設限,不想給自己貼標簽,但我會堅持自己的選擇,按照自己的節(jié)奏用心生活,我特別喜歡羅曼羅蘭的一句話——有一種英雄主義就是認清了生活的真相,卻依然熱愛它。
現(xiàn)在我把這句話也送給你,也許生活很殘酷,超出了你的認知極限,黑暗、骯臟、毀三觀,但依然有人在用心生活,深處泥淖,依然有人在仰望星空;也許生活很美好,充滿了歡聲笑語,讓你感受到滿心歡喜,但依然有人選擇放棄生命,放棄希望。倒不是說你必須成為什么樣的人,做什么事,只是希望你知道自己在做什么,自己想做什么,此時此刻你是否對當下的生活滿意,畢竟生活的點點滴滴都是選擇,遵循內(nèi)心最真實的感受,最真實的想法,或堅持,或放棄,只要你感覺舒適就好,只要你覺得值得就好,至于其他人說什么倒是無足輕重的了……
- END -