日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【算法训练】DAY1:整数反转

發(fā)布時(shí)間:2023/11/30 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【算法训练】DAY1:整数反转 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1 前言

題目來源于Leetcode。

重點(diǎn):理清邏輯,忽略細(xì)節(jié),模仿高手,五毒神掌

2 題目分析

題目很容易理解,先分成兩個(gè)部分

  • 正數(shù)
  • 負(fù)數(shù)

先解決正數(shù)

最開始想到的是

intchar數(shù)組long

唯一增加的就是,先判斷整數(shù)是多少位

之后再判斷溢出

然后解決負(fù)數(shù)
先使用一個(gè)bool變量保存符號(hào),如果是負(fù)數(shù),則取絕對(duì)值,再按正數(shù)進(jìn)行運(yùn)算,之后再加上符號(hào),再判斷溢出。

整體思維非常容易想到,分治思想,下面是代碼。

3 自己想

int reverse(int x) {// 不管正負(fù)數(shù),全變正數(shù)bool isNegativeNumber = false;int xAbsoluteValue = 0;if (x < 0) {isNegativeNumber = true;if (x != INT_MIN)xAbsoluteValue = -x;elsereturn 0;}else {isNegativeNumber = false;xAbsoluteValue = x;}// 判斷整數(shù)多少位【動(dòng)態(tài)的】int xTemporary = xAbsoluteValue;int count = 0;for (int i = 0; i < sizeof(int)*8; i++) { // 【注意】字節(jié)數(shù)*8if (xTemporary == 0) {break;}else {count++;}xTemporary /= 10;}// 反轉(zhuǎn)long long xNew = 0; // 不要用long,它在32位下也是4字節(jié)xTemporary = xAbsoluteValue;for (int i = 0; i < count; i++) {xNew = xNew * 10 + xTemporary % 10;xTemporary /= 10;}// 符號(hào)回歸if (isNegativeNumber) {xNew = -xNew;}// 判斷溢出if (xNew < INT_MIN || xNew > INT_MAX) {return 0;}else{return xNew;}}


運(yùn)行結(jié)果還可以,就是系統(tǒng)本身不穩(wěn)定,有時(shí)候是4ms,這不重要,重要的是,這種做法太啰嗦了,我先嘗試按照這個(gè)思路優(yōu)化一下。

去掉符號(hào)轉(zhuǎn)換,這部分沒有也一樣

注意使用long long,而不是long,32位下前者8字節(jié),后者和int一樣4個(gè)字節(jié)

判斷溢出,使用一行代碼搞定,取締if else,使用三元運(yùn)算符

// 判斷整數(shù)多少位【動(dòng)態(tài)的】int xTemporary = x;int count = 0;for (int i = 0; i < sizeof(int) * 8; i++) { // 【注意】字節(jié)數(shù)*8if (xTemporary == 0) {break;}else {count++;}xTemporary /= 10;}// 反轉(zhuǎn)long long xNew = 0; xTemporary = x;for (int i = 0; i < count; i++) {xNew = xNew * 10 + xTemporary % 10;xTemporary /= 10;}return (xNew < INT_MIN || xNew > INT_MAX) ? 0 : xNew;

好了,這個(gè)程序已經(jīng)優(yōu)化了很多了,還有沒有空間繼續(xù)優(yōu)化呢?

再優(yōu)化,就只能在獲取整數(shù)位數(shù)下手,將其直接變成反轉(zhuǎn)的條件使用。

好吧……我做不下去了,看大神解法好了。

談一談收獲

  • 對(duì)待算法題,重點(diǎn)關(guān)注邏輯,對(duì)于防御性編程等細(xì)節(jié)可以不用深究
  • 先把邏輯在紙面上搞清楚,再寫代碼!
  • int、long等數(shù)據(jù)類型的大小,根據(jù)系統(tǒng)位數(shù)以及編譯器決定,需要實(shí)際測(cè)試一下,盡量使用sizeof等通用的東西
  • 計(jì)算位數(shù)部分,有一點(diǎn)動(dòng)態(tài)規(guī)劃的意思,很有趣。
  • 毫無移位,按照我這么寫算法題,可以涼涼了~~~~接下來,我來學(xué)習(xí)一下大神的解法吧。

    4 看大神做法,直接模仿學(xué)會(huì)

    4.1 大神一

    long long res = 0; while (x) {res = res * 10 + x % 10;x /= 10; } return (res < INT_MIN || res > INT_MAX) ? 0 : res;

    這個(gè)大神與我的思路類似,只不過比我的又進(jìn)一步優(yōu)化,我們學(xué)習(xí)一下。

    這里最重要的一點(diǎn),不需要判斷多少位,也不需要暫存,不用管循環(huán)次數(shù),循環(huán)結(jié)束的條件,就是x = 0。

    這也不是本質(zhì),這題的本質(zhì)是數(shù)學(xué)問題

    • 是1234變成4321的問題
    • 是1234提取出每一個(gè)數(shù)字的問題

    來看看我的算法中愚蠢的點(diǎn)

    // 判斷整數(shù)多少位【動(dòng)態(tài)的】int xTemporary = x;int count = 0;for (int i = 0; i < sizeof(int) * 8; i++) { // 【注意】字節(jié)數(shù)*8if (xTemporary == 0) {break;}else {count++;}xTemporary /= 10;}// 反轉(zhuǎn)long long xNew = 0; xTemporary = x;for (int i = 0; i < count; i++) {xNew = xNew * 10 + xTemporary % 10;xTemporary /= 10;}

    關(guān)注一下兩個(gè)循環(huán)的條件

    • 循環(huán)32次,確定位數(shù)
    • 根據(jù)位數(shù)再反轉(zhuǎn)

    事實(shí)上,我想的是,先確定好位數(shù),這樣就不用每次都循環(huán)32次了,但是,我在確定位數(shù)的時(shí)候,還是循環(huán)了32次……蠢到家……

    雖然不是每次循環(huán)32次,但是,這種程序結(jié)構(gòu)無疑是垃圾的,盡管是雙重保險(xiǎn),但是沒有必要阿,我們警惕一下!

    值得警惕的結(jié)構(gòu)

    拋開題目本身,我們看一看這個(gè)結(jié)構(gòu)

    for (int i = 0; i < sizeof(int) * 8; i++) { // 【注意】字節(jié)數(shù)*8if (xTemporary == 0) {break;}else {count++;}xTemporary /= 10; }

    for循環(huán)中,嵌套一個(gè)通過if判斷的跳出循環(huán)的裝置,我們來改進(jìn)一下

    while(xTemporary){ xTemporary /= 10;count++;}

    嗯,這倆功能完全一樣,但是顯然后者更加簡(jiǎn)潔

    現(xiàn)在,我們是通過中介count來完成程序,那么,可以去掉中間商嗎?

    當(dāng)然可以!


    既然,xTemporary /= 10就可以作為終止條件,我們直接用就好了,沒必要再管中間商,忽略它!

    看一下我們剛才優(yōu)化的本質(zhì),將x /= 10;和while(x) 二者配合,作為循環(huán)終止條件,因此,我們進(jìn)一步優(yōu)化。

    // 判斷整數(shù)多少位【動(dòng)態(tài)的】int xTemporary = x;int count = 0;while(xTemporary){ xTemporary /= 10;count++;}// 反轉(zhuǎn)long long xNew = 0; while(xTemporary) {xNew = xNew * 10 + xTemporary % 10;xTemporary /= 10;}

    這樣一來,你很容易發(fā)現(xiàn),第一個(gè)循環(huán)完全沒有用,直接刪掉。

    int reverse(int x) {long long xNew = 0; while(x) {xNew = xNew * 10 + x % 10;x /= 10;}return (xNew < INT_MIN || xNew > INT_MAX) ? 0 : xNew; }

    我們,成功將自己的爛程序一步步優(yōu)化成了大神的程序。

    為了程序的通用性,我們稍改一下

    int reverse(int x) {long long xNew = 0; while(x != 0) {xNew = xNew * 10 + x % 10;x /= 10;}return (xNew < INT_MIN || xNew > INT_MAX) ? 0 : xNew; }

    因?yàn)橹挥蠧/C++使用0和false是一樣的,但是Java就不允許,只能使用布爾值。

    4.2 大神二

    int reverse(int x) {int d = 0;while (x){if (d > INT_MAX / 10 || d < INT_MIN / 10)return 0;d = d * 10 + x % 10;x = x / 10;}return d; }

    我們分析大神的思路,我先緩緩下跪了!

    在后面五毒神掌第二掌會(huì)分析。

    5 收獲

    5.1 一個(gè)重要結(jié)構(gòu)的優(yōu)化

    for循環(huán)內(nèi),通過if跳出的時(shí)候,可以優(yōu)化。

    for(int i = 0;i < sizeof(int)*8;i++){if(x){break; }x /= 10 } while(x){x /= 10 }

    5.2 去掉“中間商”的方法

    對(duì)于一些共性的東西,不再單獨(dú)列出中間結(jié)果,直接得到最終答案

    5.3 算法的本質(zhì)是數(shù)學(xué)問題

    這個(gè)數(shù)學(xué)表達(dá)式其實(shí)是這么來的

    • 先分治,拆解為數(shù)字+權(quán)重的形式,本質(zhì)是硬件思維
    • 再調(diào)換數(shù)字的權(quán)重

    至于最終的表達(dá)式,需要一點(diǎn)點(diǎn)優(yōu)化過來。

    我們需要知道,對(duì)于int x;

    • 求最低位的數(shù)字:x % 10
    • 降維,降低數(shù)量級(jí):x / 10(利用int直接抹掉小數(shù)點(diǎn))

    第一次的算法(使用偽代碼)

    while(遍歷每一位的數(shù)字){number[i] = x % 10;x /= 10; }

    這是很容易想到的,那么,我們保存了每一位數(shù)字,怎么保存它的權(quán)重?真的有必要保存權(quán)重嗎?
    顯然沒有必要,我們?cè)囈幌戮椭?#xff0c;可以直接一邊處理舊數(shù)字,一邊計(jì)算新數(shù)字

    newX = 0; while(遍歷每一位的數(shù)字){number[i] = x % 10;x /= 10;newX = newX*10 + number[i]; }

    這已經(jīng)是最小單元,沒法解釋,自己試一下吧。

    然后你會(huì)發(fā)現(xiàn)number[i]是多余的,并且遍歷的條件就是x != 0。

    long long newX = 0; while(x != 0){newX = newX*10 + x % 10;x /= 10; }

    至于為什么用long long,這叫先假想結(jié)果,因?yàn)榻Y(jié)果會(huì)溢出,所以只能用long long了。

    5.4 一些衍生的題目

    5.4.1 求整數(shù)位數(shù)

    所有整數(shù)均可。

    int reverse(int x) {int count = 0;while (x){x /= 10;count++;}return count; }

    5.4.2 求整數(shù)的每一位

    void reverse(int x) {int count = 0;int xTemporary = x;while (xTemporary){xTemporary /= 10;count++;}int *everyNumber = new int[count];for (int i = 0; i < count; i++) {everyNumber[i] = x % 10;x /= 10;}for (int i = 0; i < count; i++) {cout << everyNumber[i] << endl;} }

    注意

    char與int轉(zhuǎn)換,記得差一個(gè)'0'

    int i = 4; char a = i + '0'; cout << a << endl;

    6 五毒神掌

    五毒神掌是什么?

    關(guān)注代碼邏輯和結(jié)構(gòu)層面的細(xì)節(jié)

    目標(biāo)導(dǎo)向,一天一個(gè),完全搞定300題

    6.1 第一掌

  • 先正確理解題目
  • 自己想,5分鐘想出來就寫
  • 想不出來,就直接看世界大神答案,并且理解
  • 然后大致理解背下來(理解代替記憶,如果不理解,就先記憶,多用用就理解了)
  • 邊抄邊背的方式寫代碼
  • 自己的思路不能只有一種,每種都要嘗試。

    重點(diǎn)關(guān)注邏輯!畫圖+手算分析

    6.1.1 自己思考的過程

    題目很簡(jiǎn)單,就是整數(shù)反轉(zhuǎn),需要注意

    • 正負(fù)數(shù)問題
    • 反轉(zhuǎn)后溢出問題:用long long存儲(chǔ)


    之后用幾個(gè)數(shù)字試一試,研究一下數(shù)學(xué)公式,先寫正確,再不斷優(yōu)化。

    int reverse(int x) {long long xNew = 0;while (x != 0) {xNew = xNew * 10 + x % 10;x /= 10;}return (xNew < INT_MIN || xNew > INT_MAX) ? 0 : xNew; }

    6.1.2 大神的代碼

    public int reverse(int x) {int result = 0;while (x != 0){int tail = x % 10;int newResult = result * 10 + tail;if ((newResult - tail) / 10 != result){ return 0; }result = newResult;x = x / 10;}return result; }

    基于我的思路,如果可能溢出,就直接使用更大的容器取存儲(chǔ)數(shù)據(jù),然后看看有沒有超過小容器的值,那么,如果沒有更大的容器,又該怎么辦?

    沒有大容器,那就用2個(gè)小容器,比較新值和舊值。

    對(duì)于重點(diǎn)公式x1新 = x1舊 * 10 + x % 10,我們知道,在數(shù)學(xué)公式中,進(jìn)行等價(jià)變形,等式應(yīng)該相等,也就是等式(x1新 - x%10) / 10 = x1舊成立。

    但是對(duì)于計(jì)算機(jī)不同,如果第一個(gè)公式計(jì)算過程有溢出,就會(huì)丟失數(shù)據(jù),那么第二個(gè)公式就不成立

    這也就是我們判斷的重點(diǎn):If overflow exists, the new result will not equal previous one.

    如果溢出存在,那么,使用新值運(yùn)算反過來得到的舊值,就不是原來的那個(gè)舊值。

    代碼如下:

    int reverse(int x) {int xNew1 = 0; // 舊值int xNew2 = 0; // 新值while (x) {xNew2 = xNew1 * 10 + x % 10;if ((xNew2 - x % 10) / 10 != xNew1)return 0;xNew1 = xNew2;x /= 10;}return xNew2; }

    事實(shí)上,在Leetcode編譯器,上面的寫法是錯(cuò)誤的!

    新的收獲:使用經(jīng)典的測(cè)試用例

    不得不說……任何的算法,在使用大量測(cè)試用例測(cè)試之前,都不一定完美,例如上面的算法,如果使用INT_MAX作為測(cè)試用例,對(duì)于能夠進(jìn)行溢出檢測(cè)的嚴(yán)格編譯器來說,會(huì)出現(xiàn)報(bào)錯(cuò)(不過C++編譯器一般不檢測(cè)……),那么,報(bào)錯(cuò)的原因是什么

    我們看一下xNew2 = xNew1 * 10 + x % 10;,試想一下,我們剛才假定這個(gè)過程中,編譯器是允許溢出后直接截?cái)?#xff0c;但不會(huì)報(bào)錯(cuò),那么現(xiàn)在,我們假定,編譯器不允許溢出的發(fā)生,我們又該怎么辦?

    【思維修煉】“治未病”思想:在問題發(fā)生之前處理掉

    對(duì)于xNew2 = xNew1 * 10 + x % 10;,我們需要在溢出發(fā)生前,就檢測(cè)出來,因此有以下程序

    int reverse(int x) {int xNew = 0;while(x != 0){if(xNew > INT_MAX/10 || xNew < INT_MIN/10) return 0;xNew = xNew * 10 + x % 10;x /= 10;}return xNew;}

    更嚴(yán)格來說,是不是需要把x % 10也“治未病”呢?顯然不需要,因?yàn)?strong>不存在一個(gè)數(shù)字,乘10后沒有溢出,但是再+1就溢出了。

    思考:為什么不是>=?

    因?yàn)?#xff0c;對(duì)于極限數(shù)字214748364(也就是INT_MAX / 10),乘10之后,再加上x % 10是不可能溢出的(可以想象,如果溢出,那x % 10的結(jié)果需要 >7,那么,在這個(gè)數(shù)反轉(zhuǎn)之前,就已經(jīng)溢出了,所以不可能)。

    經(jīng)典測(cè)試案例 + 嚴(yán)格編譯器 = 優(yōu)秀算法

    對(duì)于經(jīng)典測(cè)試案例,例如本題,可以有

    123 -123 INT_MAX INT_MIN 1230000

    這些提交前的測(cè)試案例,足夠描述各種情況了。

    6.1.3 小結(jié)

  • 1個(gè)大容器與2個(gè)小容器
  • 算法與數(shù)學(xué)公式
  • 6.2 第二掌

    把大神的代碼完全不看的情況下寫出來。

    • 搞定

    自己的代碼,多種寫法,不斷優(yōu)化到極致。

    6.3 第三掌

    過了24 小時(shí)的時(shí)間以后,再次重復(fù)做題

    不同解法的熟練程度 ——> 專項(xiàng)練習(xí)

    • 新的收獲

    6.3.1 整數(shù)反轉(zhuǎn)圖解——安檢排隊(duì)模型


    如果你從動(dòng)態(tài)的角度去看一下,是不是像一個(gè)U型排隊(duì)區(qū)人員流動(dòng)的樣子?

    想一想你過安檢排隊(duì)的情形。

    怎么樣,是不是瞬間記住了這個(gè)整數(shù)反轉(zhuǎn)模型

    int reverse(int x) {int xNew = 0;while(x){if(xNew > INT_MAX/10 || xNew < INT_MIN/10) return 0; // 預(yù)測(cè)xNew = xNew*10 + x%10;x /= 10;}return xNew;}

    通過預(yù)測(cè)提高性能

    這是偉大計(jì)算機(jī)思想之一,應(yīng)用廣泛,例如指令操作的分支預(yù)測(cè),在本題中,溢出的檢測(cè)就使用了預(yù)測(cè)思想。

    6.4 第四掌

    過了一周之后: 反復(fù)回來練習(xí)相同的題目

    6.5 第五掌

    面試前一周恢復(fù)性的訓(xùn)練,所有題目全都刷一遍

    總結(jié)

    以上是生活随笔為你收集整理的【算法训练】DAY1:整数反转的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。