Algorithm
本周选择的算法题是:Integer to English Words
规则如下:
Convert a non-negative integer to its english words representation. Given input is guaranteed to be less than \(2^{31}-1\).
Example 1:
Input: 123
Output: "One Hundred Twenty Three"
Example 2:
Input: 12345
Output: "Twelve Thousand Three Hundred Forty Five"
Example 3:
Input: 1234567
Output: "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
Example 4:
Input: 1234567891
Output: "One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven Thousand Eight Hundred Ninety One"
Solution
我实现的方案:
Runtime:32 ms,快过 97.28%。
Memory:13.2 MB,低于 52.89%。
class Solution:
num_map = {
1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five",
6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", 10: "Ten",
11: "Eleven", 12: "Twelve", 13: "Thirteen", 14: "Fourteen",
15: "Fifteen", 16: "Sixteen", 17: "Seventeen", 18: "Eighteen", 19: "Nineteen",
20: "Twenty", 30: "Thirty", 40: "Forty", 50: "Fifty", 60: "Sixty",
70: "Seventy", 80: "Eighty", 90: "Ninety",
}
carry_map = {
100: "Hundred",
1000: "Thousand",
100000: "Hundred",
1000000: "Million",
100000000: "Hundred",
1000000000: "Billion",
}
def numberToWords(self, num: int) -> str:
if num == 0:
return "Zero"
result = None
carry = 1
while num > 0:
num_bit = num % 1000
if num_bit > 0:
high = int(num_bit / 100)
low_digits = num_bit % 100
if low_digits < 20:
middle = low = 0
else:
middle = int(num_bit / 10) % 10
low = num_bit % 10
low_digits = 0
low_carry = Solution.carry_map.get(carry)
for word in [
None if low_carry is None else low_carry,
None if low_digits == 0 else Solution.num_map.get(low_digits),
None if low == 0 else Solution.num_map.get(low),
None if middle == 0 else Solution.num_map.get(middle * 10),
None if high == 0 else Solution.num_map.get(high) + " " + Solution.carry_map.get(carry * 100),
]:
if word is not None:
result = word + ("" if result is None else (" " + result))
num = int(num / 1000)
carry *= 1000
return result
我在内存中放置了两张表,一张对应数字,一张对应进位。
在 numberToWords
里将数字每三位作为一个整体单独转换:
- 将 123456789 从低位开始分解成 789、456、123 三个区间
- 如果区间的三个数字为0,直接跳过,如 1000001 转换为 “One Million One”
- 区间不为0,将数字转换为对应的 words,补上单位
- Done
按三位分解,独自转换,然后拼接在一起,进位表需要将单位全部覆盖。
Review
Let’s Talk Architechture
我很喜欢作者的观点。全文总结了 iOS 过去这些年架构演化的过程:
The Past
- MVC,不同于经典的 MVC,Apple 的 MVC 名声可不好,原本的 View 层是可直接与 Model 层通信,但是 Apple 将 Controller 层作为胶水粘合 View 和 Model,最终让 Controller 变成一个庞然大物,3层架构的 MVC 看起来像是2层架构
- The Present
- MVVM,这是在 iOS 开发者之间广为流传的第一个替代方案,它是一个3层架构,相比之下,它的每一层都很容易测试,你不再需要构建一个 View 去测试 ViewModel,触发一些行为并记录下结果就可以很轻松的完成测试。它也有缺点,View 与 ViewModel 的粒度不好把握,View 与 View 之间的动画不好联动
- MVP,Apple 的架构图虽然是 MVC,但更像是 MVP,这也是一种在 iOS 之外很流行的架构,很老,比 MVVM 更老,但是它也是一个3层架构,只要理解了 Controller 或 Presenter 不必须是 ViewController 就能让架构变得更清晰、易维护。事实上我使用 MVP 已经很多年了,大概从14、15年开始,做了很多基于 MVP 的设计,引入了双向数据绑定等,目前来说都还是很好用的方案
- The Future
Elm,作者展望未来,认为像 React、Vue 这种具有 Diff 算法局部更新 UI 的方式可能会成为未来的方向。Elm 包含三个基本的部分:
- Model,程序状态
- Update,更新状态
- View,展示状态
这是一个标准的3层架构,其实所有的架构无非就是三种角色:
- 数据管理者
- 数据加工者
- 数据展示者
UI 绑定是现在架构处理的难点,拭目以待吧。
Tip
本周学习到的一些内容:
- JS 动态属性名:
const dynamic = 'email'; let user = { name: 'John', [dynamic]: 'john@doe.com' } console.log(user); // outputs { name: "John", email: "john@doe.com" }
- 响应式函数编程
- 简单却强大
- 申明式的步骤让代码更容易理解
- 让复杂的流程变得容易管理和表示
- 冷信号,还没有开始传递事件,每一个新的订阅都会开启一个信号
- 热信号,已经开始传递事件,新的订阅者不会开启一个信号
Share
本周分享 ReactiveCocoa vs RxSwift,虽然是一篇老文,语法层面可能已经过时,但是观点依然值得一看。
大家都喜欢 Nike,但是为什么还有人穿着 Adidas :)
Why are developers still using Angular?,这篇文章让我开始反思 Angular 有哪些优势是 React 所不具备的,以往谈起 Angular 时总是会想起它陡峭的学习曲线以及老用户不如狗的更新策略,框架都有自己擅长的一面,不如放下比较,从一次创作开始感受它们各自的美妙。