花式求解 LeetCode 279題-Perfect Squares

迫于就業(yè)的壓力尿招,不得不先放下 iOS 開發(fā)的學(xué)習(xí)券腔,開始走上漫漫刷題路畔规。

今天我想聊聊 LeetCode 上的第279題-Perfect Squares局扶,花了挺長(zhǎng)時(shí)間的,試了很多方法叁扫,作為一個(gè)算法新手三妈,個(gè)人感覺這題很好,對(duì)我的水平提升很有幫助莫绣。我在這里和大家分享一下我的想法畴蒲。下面是題目:

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ... ) which sum to n.
For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13,return 2 because 13 = 4 + 9.

大致意思就是,“給一個(gè)正數(shù) n对室, 找到和為 n 的平方數(shù), 給出最少的平方數(shù)個(gè)數(shù)”模燥。

BFS

我剛開始想到的是用 BFS,經(jīng)過一番實(shí)踐掩宜,感覺代碼是對(duì)的蔫骂,但是 Time Limit Exceeded。畢竟用了 2 層循環(huán)牺汤。于是我就找了個(gè)字典(Dictionary)來存已經(jīng)算過的節(jié)點(diǎn)辽旋,比如一個(gè)很大的數(shù) n,有很大幾率 n - i * i 這個(gè)節(jié)點(diǎn)和后面算出來的 m - j * j 是相等的檐迟。那么就不再重新計(jì)算补胚。但是,還是超時(shí)了追迟。這部分代碼2個(gè)小時(shí)前被我扔了溶其,我就不在這里重新寫了。

Lagrange's four-square theorem

這里算是完全用數(shù)學(xué)知識(shí)解決了這個(gè)問題敦间。不知道四平方和定理的請(qǐng)參考 wikipedia握联。話說童鞋們最好看英文版的 wiki桦沉,別翻譯成中文比較好。我也不說英文更專業(yè)金闽,雖然好像就是這么回事 == 因?yàn)橛袀€(gè)公式非常重要纯露,而解這題全靠這個(gè)公式:


這個(gè)定理就是講,任何數(shù)都可以由4個(gè)平方數(shù)組成代芜,即 n = a^2 + b^2 + c^2 + d^2埠褪,所以這題的答案已經(jīng)限定在了 [1,4] 之間。

而上面這個(gè)公式的發(fā)明者-Adrien-Marie Legendre 又補(bǔ)充了這個(gè)定理:除了滿足以上這個(gè)公式的數(shù)以外的任何數(shù)都可以由3個(gè)平方數(shù)組成挤庇。所以钞速,這個(gè)答案又可以縮小范圍了。范圍都已經(jīng)縮小到 [1,3] 了嫡秕,我們開始求解渴语。

先排除4個(gè)的情況:

    while myN & 3 == 0 {
        myN >>= 2
    }

    if myN % 8 == 7 {
        return 4
    }

因?yàn)?和2的情況比較容易排除,先把1和2的排除昆咽。

    var index = Int(sqrt(Double(n)))
    while index > 0 {
        let tmp = Double(n - index * index)
        let sqrtTmp = Int(sqrt(tmp))
        if n == sqrtTmp * sqrtTmp + index * index {
            return sqrtTmp == 0 ? 1 : 2
        }
        index -= 1
    }

上面的代碼就是說驾凶,如果一個(gè)數(shù)由2個(gè)平方數(shù)組成,如果其中一個(gè)平方數(shù)是0掷酗,那么就是1调违,如果不是0,那就是2泻轰。

剩下的就是3了技肩,直接 return 3 就行了。在知道這個(gè)數(shù)學(xué)公式的情況下浮声,這個(gè)方法還是很簡(jiǎn)單的虚婿。

DP

我剛刷題沒幾天,對(duì)于 DP 的推理過程還不是很熟練泳挥,琢磨了好久雳锋。一旦琢磨出來了,又覺得好簡(jiǎn)單羡洁,換一題玷过,又可以琢磨一年。lol

初級(jí)的 DP 的使用方式差不多就是 Recursion + Memorization筑煮,就是遞歸和緩存辛蚊。這里我們用一個(gè)數(shù)組來存儲(chǔ)已經(jīng)算過的數(shù)的最少平方數(shù)的個(gè)數(shù) (記作 minNum)。從1開始算(從0也沒事)真仲。

這里我們分2層來算袋马,外層循環(huán)是計(jì)算從1到 n的各個(gè)數(shù)的最少平方數(shù) minNum, 存入到數(shù)組中,數(shù)組的 index 表示數(shù) n秸应,里面的 val 表示 minNum虑凛。關(guān)鍵是求每個(gè)數(shù)的 minNum碑宴。這里我們用到遞歸,核心代碼就是:

let tmp = val - i * i
minNum = min(minNum, tmp == 0 ? 1 : 1+sta.record[tmp])

tmp 表示 val 減去一個(gè)平方數(shù)剩下的數(shù)桑谍,如果 tmp == 0延柠,就表示 val == i * i,即它由1個(gè)平方數(shù)組成锣披;如果 tmp != 0贞间,就那么我們就需要求以 tmp 為 val 的 minNum,也就是 tmp2 = tmp - i * i 雹仿,這個(gè) tmp2 就相當(dāng)于之前的 tmp增热。為了求 tmp 的 minNum,我們需要計(jì)算出 從1到 sqrt(val) 之間所有的可能值胧辽,然后取最小值峻仇。最后將那個(gè)最小值存放到數(shù)組中。最終代碼就是

func numSquares(n: Int) -> Int {
     var record = [0,1]
    while record.count <= n {
        var val = record.count, minNum = record.count
        for i in 1...Int(sqrt(Double(val))) {
            let tmp = val - i * i
            minNum = min(minNum, tmp == 0 ? 1 : 1+record[tmp])
        }
        record.append(minNum)
    }
    return record[n]
}

但是跑了之后又發(fā)現(xiàn)邑商,我特喵的沒錯(cuò)啊,怎么時(shí)間又是這么長(zhǎng)奠骄,1400ms。如果拿個(gè)稍微大點(diǎn)的數(shù)放到 playground 里跑一跑就會(huì)發(fā)現(xiàn)番刊,循環(huán)次數(shù)還是挺多的含鳞。所以這里就需要考慮到把數(shù)組存成 static,而 swift 是沒法在 function 里直接申明 static var n = 1 的芹务,我們需要把 static 放在 class/struct 里蝉绷,參見 SO 大神的解答,還有官方 doc枣抱。

可以把這個(gè) struct 放在 class Solution 里面熔吗,也可以放在外面,最后時(shí)間是 60ms 左右佳晶。從 1400 到 60桅狠,還是可以的。

struct sta {
    static var record = [0,1]
}

也許從短短這么一篇文章你就已經(jīng)看出來了一些 swift 語言的特點(diǎn)轿秧,最大的特點(diǎn)就是類型安全中跌。求個(gè)根都要 Int(sqrt(Double(n))),我以前是用 C++ 的菇篡,遇到這種情況還是有點(diǎn)膈應(yīng)的漩符。但其實(shí) swift 的優(yōu)點(diǎn)絕對(duì)是可以讓我安全無視這些小麻煩的,其實(shí)習(xí)慣了之后就感覺是更方便驱还,更安全了嗜暴。

最后

每篇文章我都在用心寫凸克,希望志同道合的童鞋能一起學(xué)習(xí)一起進(jìn)步。如果喜歡我就請(qǐng)關(guān)注我哦闷沥,點(diǎn)個(gè)??表示鼓勵(lì)吧~

最近貌似 RESTful 很火萎战,如果你對(duì) MongoDB 或者 RESTful 感興趣,請(qǐng)看我的這篇文章狐赡,我用 MongoDB 作為后臺(tái)數(shù)據(jù)庫撞鹉,用 AngularJS, Spark, Java 做了個(gè)網(wǎng)站 demo,建于 Heroku 上颖侄。每一種技術(shù)都是當(dāng)下最流行的技術(shù)鸟雏。

最后強(qiáng)烈推薦喜歡 swift,并想用 swift 寫算法的童鞋览祖,Swift Algorithm Club孝鹊,你值得擁有。


歡迎轉(zhuǎn)載展蒂,轉(zhuǎn)載請(qǐng)注明出處又活。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锰悼,隨后出現(xiàn)的幾起案子柳骄,更是在濱河造成了極大的恐慌,老刑警劉巖箕般,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耐薯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡丝里,警方通過查閱死者的電腦和手機(jī)曲初,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杯聚,“玉大人臼婆,你說我怎么就攤上這事』仙埽” “怎么了颁褂?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)傀广。 經(jīng)常有香客問我痢虹,道長(zhǎng),這世上最難降的妖魔是什么主儡? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任奖唯,我火速辦了婚禮,結(jié)果婚禮上糜值,老公的妹妹穿的比我還像新娘丰捷。我一直安慰自己坯墨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布病往。 她就那樣靜靜地躺著震蒋,像睡著了一般猜丹。 火紅的嫁衣襯著肌膚如雪嫉柴。 梳的紋絲不亂的頭發(fā)上恕酸,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音畔勤,去河邊找鬼蕾各。 笑死,一個(gè)胖子當(dāng)著我的面吹牛庆揪,可吹牛的內(nèi)容都是我干的式曲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼缸榛,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吝羞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起内颗,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤钧排,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后均澳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恨溜,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年负懦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了筒捺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柏腻。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纸厉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出五嫂,到底是詐尸還是另有隱情颗品,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布沃缘,位于F島的核電站躯枢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏槐臀。R本人自食惡果不足惜锄蹂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望水慨。 院中可真熱鬧得糜,春花似錦敬扛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至治宣,卻和暖如春急侥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背侮邀。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工坏怪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人豌拙。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓陕悬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親按傅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捉超,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容