Algorithm
本周选择的算法题是:Generate Parentheses
规则如下:
Given n
pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
Example 1:
Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
Example 2:
Input: n = 1
Output: ["()"]
Constraints:
1 <= n <= 8
Solution
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
ans = []
def backtrack(item: str, left: int, right: int):
if len(item) == 2 * n:
ans.append(item)
return
if left < n:
backtrack(f"{item}(", left + 1, right)
if right < left:
backtrack(f"{item})", left, right + 1)
pass
backtrack("", 0, 0)
return ans
Review
2 Most Powerful Ways to Remember Everything You Learn
这篇文章先简要介绍了人的记忆系统,然后引出了艾宾浩斯遗忘曲线,最后介绍了两种提高记忆效率的方式:
- 间歇性的重复 - 市面上很多背英语的软件采用了这种方式,不断的回顾已学习到的知识来提高大脑对它的重要性评级
- 50/50 法则 - 把学习周期分为两部分,50%的时间用来学习,50%的时间用来分享或教授给别人
这两种方式都是帮助大脑对知识进行分类,现代社会的信息量太大了,大脑必须过滤掉很多信息以释放更多的内存给重要的部分,这意味着大脑要处理的数据量比以往更多,如果我们想留下真正重要的东西,必须得掌握一定的训练技巧。
Tip
推荐一个有趣的打卡社区:CheckiO,相比 LeetCode:
- 游戏化
- 问题类型更丰富
- 不限制解题方式
- 提示极具引导性:
- 可以通过 Review 别人的代码找到一些骚操作
目前支持 Python 和 TypeScript 这两种语言。
Share
大家是否有过和其他人关于某个场景该用哪个设计模式的争论?
“我更倾向于用 MVVM”
“这个场景用 VIPER 更合适”
“MVC 就足够了”
“…”
这类讨论很难有一个完美的结束,这类讨论也永远不会停止。
我们常在一些场合听到别人用这样的分享开场白:
“我们这个系统采用了XXX设计模式”
“它采用XXX架构”
似乎在系统初始阶段就做了很清晰、完备的设计,并沿用至今,其实这是一个很大的误区,程序本身也是有生命力的,它会不断演进,这意味着很难在初始阶段就知道什么模式会适合你的场景,同时如果你对模式本身太过于关注,可能会陷入模式的陷阱中:我的代码一定要遵守XXX模式,哪怕和现有的场景有点不贴合了。
那要如何避免这些问题呢?
每个模式都有自己的价值,它们都是为某种常见的场景提供了通用的解决方案,虽然不同,但是它们背后的原则是一致的,即:
- 模块化
- 可重用性
- 易读
- 易修改
我们只需要在代码上应用这些原则,模式就会自然体现。我们不用太关注模式本身,因为模式热度会降低,而原则的生命力会很久;模块化,尽可能编写可重用的代码,维护合适的文档,就足以让我们写出出色的代码。
此外,真正理解代码是给人读的这一点很重要,不需要很 fancy,因为:
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
— Martin Fowler
当然,如果你的代码既 fancy 也很容易理解,你就很棒。
最后总结一下,我们并不是说设计模式没用,而是希望能更进一步,了解「Why」,而不是只停留在「What」,真正理解你所使用的模式背后的原则,用好的编程设计原则去发现模式,因为原则才是每一个模式背后的基础。