花式求解 LeetCode 279题-Perfect Squares

迫于就业的压力,不得不先放下 iOS 开发的学习,开始走上漫漫刷题路。

今天我想聊聊 LeetCode 上的第279题-Perfect Squares,花了挺长时间的,试了很多方法,作为一个算法新手,个人感觉这题很好,对我的水平提升很有帮助。我在这里和大家分享一下我的想法。下面是题目:

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.

大致意思就是,“给一个正数 n, 找到和为 n 的平方数, 给出最少的平方数个数”。

BFS

我刚开始想到的是用 BFS,经过一番实践,感觉代码是对的,但是 Time Limit Exceeded。毕竟用了 2 层循环。于是我就找了个字典(Dictionary)来存已经算过的节点,比如一个很大的数 n,有很大几率 n - i * i 这个节点和后面算出来的 m - j * j 是相等的。那么就不再重新计算。但是,还是超时了。这部分代码2个小时前被我扔了,我就不在这里重新写了。

Lagrange's four-square theorem

这里算是完全用数学知识解决了这个问题。不知道四平方和定理的请参考 wikipedia。话说童鞋们最好看英文版的 wiki,别翻译成中文比较好。我也不说英文更专业,虽然好像就是这么回事 == 因为有个公式非常重要,而解这题全靠这个公式:


这个定理就是讲,任何数都可以由4个平方数组成,即 n = a^2 + b^2 + c^2 + d^2,所以这题的答案已经限定在了 [1,4] 之间。

而上面这个公式的发明者-Adrien-Marie Legendre 又补充了这个定理:除了满足以上这个公式的数以外的任何数都可以由3个平方数组成。所以,这个答案又可以缩小范围了。范围都已经缩小到 [1,3] 了,我们开始求解。

先排除4个的情况:

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

    if myN % 8 == 7 {
        return 4
    }

因为1和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
    }

上面的代码就是说,如果一个数由2个平方数组成,如果其中一个平方数是0,那么就是1,如果不是0,那就是2。

剩下的就是3了,直接 return 3 就行了。在知道这个数学公式的情况下,这个方法还是很简单的。

DP

我刚刷题没几天,对于 DP 的推理过程还不是很熟练,琢磨了好久。一旦琢磨出来了,又觉得好简单,换一题,又可以琢磨一年。lol

初级的 DP 的使用方式差不多就是 Recursion + Memorization,就是递归和缓存。这里我们用一个数组来存储已经算过的数的最少平方数的个数 (记作 minNum)。从1开始算(从0也没事)。

这里我们分2层来算,外层循环是计算从1到 n的各个数的最少平方数 minNum, 存入到数组中,数组的 index 表示数 n,里面的 val 表示 minNum。关键是求每个数的 minNum。这里我们用到递归,核心代码就是:

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

tmp 表示 val 减去一个平方数剩下的数,如果 tmp == 0,就表示 val == i * i,即它由1个平方数组成;如果 tmp != 0,就那么我们就需要求以 tmp 为 val 的 minNum,也就是 tmp2 = tmp - i * i ,这个 tmp2 就相当于之前的 tmp。为了求 tmp 的 minNum,我们需要计算出 从1到 sqrt(val) 之间所有的可能值,然后取最小值。最后将那个最小值存放到数组中。最终代码就是

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]
}

但是跑了之后又发现,我特喵的没错啊,怎么时间又是这么长,1400ms。如果拿个稍微大点的数放到 playground 里跑一跑就会发现,循环次数还是挺多的。所以这里就需要考虑到把数组存成 static,而 swift 是没法在 function 里直接申明 static var n = 1 的,我们需要把 static 放在 class/struct 里,参见 SO 大神的解答,还有官方 doc

可以把这个 struct 放在 class Solution 里面,也可以放在外面,最后时间是 60ms 左右。从 1400 到 60,还是可以的。

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

也许从短短这么一篇文章你就已经看出来了一些 swift 语言的特点,最大的特点就是类型安全。求个根都要 Int(sqrt(Double(n))),我以前是用 C++ 的,遇到这种情况还是有点膈应的。但其实 swift 的优点绝对是可以让我安全无视这些小麻烦的,其实习惯了之后就感觉是更方便,更安全了。

最后

每篇文章我都在用心写,希望志同道合的童鞋能一起学习一起进步。如果喜欢我就请关注我哦,点个♥️表示鼓励吧~

最近貌似 RESTful 很火,如果你对 MongoDB 或者 RESTful 感兴趣,请看我的这篇文章,我用 MongoDB 作为后台数据库,用 AngularJS, Spark, Java 做了个网站 demo,建于 Heroku 上。每一种技术都是当下最流行的技术。

最后强烈推荐喜欢 swift,并想用 swift 写算法的童鞋,Swift Algorithm Club,你值得拥有。


欢迎转载,转载请注明出处

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容