原文地址

https://www.jianshu.com/p/2925f4d7511b

迫于就业的压力,不得不先放下 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
}
<span class="hljs-keyword">if</span> myN % <span class="hljs-number">8</span> == <span class="hljs-number">7</span> {
<span class="hljs-keyword">return</span> <span class="hljs-number">4</span>
}

因为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,你值得拥有。


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

      </div>

花式求解 LeetCode 279题-Perfect Squares的更多相关文章

  1. LeetCode 279. 完全平方数(Perfect Squares) 7

    279. 完全平方数 279. Perfect Squares 题目描述 给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n.你需要让组成和的完全平方数 ...

  2. LeetCode算法题-Perfect Number(Java实现)

    这是悦乐书的第249次更新,第262篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第116题(顺位题号是507).我们定义Perfect Number是一个正整数,它等于 ...

  3. LeetCode算法题-Magic Squares In Grid(Java实现)

    这是悦乐书的第326次更新,第349篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第196题(顺位题号是840).3 x 3魔方是一个3 x 3网格,填充了从1到9的不同 ...

  4. LeetCode(279)Perfect Squares

    题目 Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9 ...

  5. LeetCode OJ:Perfect Squares(完美平方)

    Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 1 ...

  6. Leetcode之广度优先搜索(BFS)专题-279. 完全平方数(Perfect Squares)

    Leetcode之广度优先搜索(BFS)专题-279. 完全平方数(Perfect Squares) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ar ...

  7. [LeetCode] 279. Perfect Squares 完全平方数

    Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 1 ...

  8. [LeetCode 279.] Perfect Squres

    LeetCode 279. Perfect Squres DP 是笨办法中的高效办法,又是一道可以被好办法打败的 DP 题. 题目描述 Given a positive integer n, find ...

  9. LeetCode Perfect Squares

    原题链接在这里:https://leetcode.com/problems/perfect-squares/ 题目: Given a positive integer n, find the leas ...

随机推荐

  1. Ethical Hacking - Web Penetration Testing(12)

    XSS VULNS XSS - CROSS SITE SCRIPTING VULNS Allow an attacker to inject javascript code into the page ...

  2. Oracle修改表类型方法

    有一个表名为tb,字段段名为name,数据类型nchar(20). 1.假设字段数据为空,则不管改为什么字段类型,可以直接执行:alter table tb modify (name nvarchar ...

  3. python爬虫入门(1)----- requests

    介绍 requests是python实现的简单易用的HTTP库,使用起来比urllib简洁很多 基本使用 requests.get("http://www.baidu.com") ...

  4. Invalid RNPermission 'ios.permission.xxx'. should be one of: ( )

    原因可能是配置配置问题, 我碰到的是Android上完美运行,iOS报错,原因是前期用的Android开发,iOS的配置项没有配完整 按照官方配置一遍 https://github.com/react ...

  5. 【Unity3D】简单常用的功能实现1——鼠标点击实现移动

    [鼠标点击实现移动] 第一步,在要移动的GameObject的Inspector面板中添加角色控制器组件并调整参数. 参数的调整可以依照Scene面板,如下图的绿色线框(角色控制器组件是一种胶囊形状的 ...

  6. DQL_MySQL

    4.DQL(查询数据){SUPER 重点} 4.1DQL (Data Query Language : 数据查询语言) -所有的查询操作: Select 数据库中最核心的语言  create data ...

  7. IDEA中配置Project Structure

    本文主要介绍在IDEA中怎么配置项目Project Structure. 若文中有所偏错,望能够留言指正,不胜感激. 不再赘述,直接进入正题: 1. 打开IDEA的Project Structure( ...

  8. PHP date_modify() 函数

    ------------恢复内容开始------------ 实例 修改时间戳.增加 15 天: <?php$date=date_create("2013-05-01");d ...

  9. PHP atan() 函数

    实例 通过 atan() 函数返回不同数的反正切: <?phpecho(atan(0.50) . "<br>");echo(atan(-0.50) . " ...

  10. PHP mt_getrandmax() 函数

    实例 返回通过调用 mt_rand() 函数显示的随机数的最大可能值: <?phpecho(mt_getrandmax()); ?>高佣联盟 www.cgewang.com 定义和用法 m ...