花式求解 LeetCode 279题-Perfect Squares
原文地址
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的更多相关文章
- LeetCode 279. 完全平方数(Perfect Squares) 7
279. 完全平方数 279. Perfect Squares 题目描述 给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n.你需要让组成和的完全平方数 ...
- LeetCode算法题-Perfect Number(Java实现)
这是悦乐书的第249次更新,第262篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第116题(顺位题号是507).我们定义Perfect Number是一个正整数,它等于 ...
- LeetCode算法题-Magic Squares In Grid(Java实现)
这是悦乐书的第326次更新,第349篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第196题(顺位题号是840).3 x 3魔方是一个3 x 3网格,填充了从1到9的不同 ...
- LeetCode(279)Perfect Squares
题目 Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9 ...
- LeetCode OJ:Perfect Squares(完美平方)
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 1 ...
- Leetcode之广度优先搜索(BFS)专题-279. 完全平方数(Perfect Squares)
Leetcode之广度优先搜索(BFS)专题-279. 完全平方数(Perfect Squares) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ar ...
- [LeetCode] 279. Perfect Squares 完全平方数
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 1 ...
- [LeetCode 279.] Perfect Squres
LeetCode 279. Perfect Squres DP 是笨办法中的高效办法,又是一道可以被好办法打败的 DP 题. 题目描述 Given a positive integer n, find ...
- LeetCode Perfect Squares
原题链接在这里:https://leetcode.com/problems/perfect-squares/ 题目: Given a positive integer n, find the leas ...
随机推荐
- Python Ethical Hacking - Malware Analysis(4)
DOWNLOAD_FILE Download files on a system. Once packaged properly will work on all operating systems. ...
- super,this关键字用法 Java
super 用法 1.调用父类变量2.调用父类方法3.子类构造方法第一句 this 用法 super关键字用来访问父类内容, this 关键字用来访问本类中的内容, 有三种用法 1.在本类的成员方法中 ...
- 3c数码商城
目标:2020样卷 已完成:增删改查 未完成:有些小知识点不在意丢失,因此导致有些未完善 解决方案:写代码时不要走心,专心一点,减少失误,减少时间,增加效率,使自己的项目看起来更优秀,注意小知识的掌握 ...
- Flutter学习笔记(41)--自定义Dialog实现版本更新弹窗
如需转载,请注明出处:Flutter学习笔记(41)--自定义Dialog实现版本更新弹窗 功能点: 1.更新弹窗UI 2.强更与非强更且别控制 3.屏蔽物理返回键(因为强更的时候点击返回键,弹窗会消 ...
- 今天上午完成了devicescan,发送了rar包到yzx3233@sina.com
今天上午完成了devicescan,发送了rar包到yzx3233@sina.com 可以正常扫描和输入了. 还有一个最后的问题,就是选择退出后,程序还在后台
- MacOS下VUEJS简单入门
微信搜索"艺术行者",关注并回复关键词"vue"获取课程资料 上传的在线学习视频(黑马和传智双元,感谢) VueJs概述与快速入门 学习之前假设你已了解关于 H ...
- numpy第三方库
# 导入numpy 并赋予别名 np import numpy as np # 创建数组的常用的几种方式(列表,元组,range,arange,linspace(创建的是等差数组),zeros(全为 ...
- JavaScript Set对象
JavaScript Set对象 Set 用于存储任何类型的唯一值,无论是基本类型还是引用类型. 只有值没有键 严格类型检测存储,字符串数字不等同于数值型数字 存储的值具有唯一性 遍历顺序是添加的顺序 ...
- PHP similar_text() 函数
实例 计算两个字符串的相似度并返回匹配字符的数目: <?php高佣联盟 www.cgewang.comecho similar_text("Hello World",&quo ...
- 一本通 高手训练 1781 死亡之树 状态压缩dp
LINK:死亡之树 关于去重 还是有讲究的. 题目求本质不同的 具有k个叶子节点的树的个数 不能上矩阵树. 点数很少容易想到装压dp 考虑如何刻画树的形状 发现一个维度做不了 所以. 设状态 f[i] ...