ARTS是什么?
Algorithm:每周至少做一个leetcode的算法题;
Review:阅读并点评至少一篇英文技术文章;
Tip:学习至少一个技术技巧;
Share:分享一篇有观点和思考的技术文章。
Algorithm
LC 222. Count Complete Tree Nodes
题目解析:
输入是一个 完全二叉树,让你求出树上的节点个数。这里先说一下完全二叉树的定义。如果我们从根节点的那一层开始,按从左到右,从上到下的顺序放置节点,直到把所有节点放置完毕,那么这个树就是完全二叉树,例如:
1
/ \
2 3
/ \ /
4 5 6
很容易发现,完全二叉树有一个性质是:除了后两层,其余层上的节点都有左右两个子树。而且除叶子节点以外,其余节点的子节点只会有两种情况:
- 既有左节点,又有右节点
- 只有左节点
当然,这道题目最直接的解法就是遍历一遍树,然后得出节点个数。这种解法对任何二叉树都奏效,因为要遍历到所有的节点,时间复杂度就会是 O(n)。但仔细想一想,这种解法并不是这道题想要考察的点,毕竟我们没有很好地利用题目给出的树的特殊性质。
其实这道题目最纠结的地方在于,我们并不知道最后一层被填了多少个节点。如果知道了的话,这道题用一个数学式子就可以算出来:
节点数 = 最后一层的节点数 + 2^(h-1) - 1
这里的 h 表示的是树的层数。
无论如何,有一点我们可以确定的是,每一层的节点是从左向右填的,如果我们把这个树从根节点处拆成左子树和右子树,那么说不定拆出来的两个树会存在满二叉树,满二叉树是完全二叉树的一个特殊情况,它的最后一层是被填满的,比如:
1 1
/ \ 左 右
2 3 2 3
/ \ / / \ /
4 5 6 4 5 6
可以看到,上面拆出来的左子树就是一个满二叉树,它的节点树就可以用式子直接算出来。那么具体怎么实现呢?我们可以分别找到左子树的最左边的叶子节点,还有右子树的最左边的叶子节点,然后分别看这两个节点的层数,会存在以下两种情况:
- 所在层数相同,表示左子树最后一行是被填满的,我们可以直接计算得到左子树的节点个数
- 所在层数不相同,表示右子树最后一行是被填满的,我们可以直接计算得到右子树的节点个数
每次切分,我们都可以将树一分为二,这样时间复杂度就是 O(lgn)。最后来总结一下这道题目,在解二叉树相关的题目的时候,拆分的思想很重要,二叉树比较直观,只能拆成左子树和右子树。拆完后再看子树上面会不会有一些不同的性质。
参考代码:
func countNodes(root *TreeNode) int {
if root == nil {
return 0
}
// 分别计算左子树和右子树的最左边的节点所在的层数
left, right := leftHeight(root.Left), leftHeight(root.Right)
// 如果所在层数相同,可以确定左子树最后一层被填满
// 这样左子树的节点个数可以通过公式计算出来,右子树继续遍历
if left == right {
// 1 << left 表示的是左子树节点的个数加上根节点
return countNodes(root.Right) + (1 << left)
}
// 如果所在层数不相同,也就是左子树可能没有被填满
// 但是此时可以确定的是,右子树是填满了的,因此可以直接计算出右子树的节点个数
return countNodes(root.Left) + (1 << right)
}
func leftHeight(root *TreeNode) int {
pointer, height := root, 0
for pointer != nil {
pointer = pointer.Left
height++
}
return height
}
Review
Medium 上的一篇文章,读了感觉受益匪浅。
文章想要解决的问题
生活在当今互联网时代,你会发现之前很火的职业或者是技术慢慢地被取代,甚至是消失。比如之前的工厂流水线上的操作工人已经被自动化的机器所取代;再早些时候,手机完全是一个奢侈品,但现如今智能手机已经普通的不能再普通的设备。随着科技的发展,人们的生活变得越来越便利。但是,反过来看,如果你要在这个社会上立足,有一个不错的职业生涯,你需要学的东西也是越来越多。
作为一个程序员,要学的东西多,这个很能感同身受。那么这里问题就是,如何才能更好地掌握新的技术让自己不被时代所淘汰呢? 文章讲述了一些意识层面,或者说是认知层面的东西,这有助于我们认识到前方的路。我们说选择比努力更重要,这篇文章在某种程度上就是在帮助你做出更好的选择。
掌握新技能的六大策略
-
理智的选择自己要学的东西
在你学一项技能或者是技术时,第一步需要思考的就是 “我要学什么”。在很多地方都提到,真正掌握一项技能需要 10000 小时。因为我们的时间有限,所以我们不可能做到各个领域都精通。那么做一个好的选择就极为重要。
那怎么选择要学的东西呢?文章给的建议是 从自身出发,想想看哪些东西能够帮助你实现你最终的目标。然后,也是最重要的,不管学习的结果或者说是成果如何,你都能很享受学习的这一个过程。基于上面提到的这两点,如果一个技能或者是技术,对你实现你当前的人生目标有帮助,并且在学习的过程中你并不会感到有压力。那么就说明你做出的是对的选择。
你可能会说,现在我也不知道我感兴趣的是什么,甚至说我也不清楚我的人生目标是什么。那怎么办呢?的确,很多时候我们只能依附于环境,环境一变导致我们被迫做出很多改变,换句话说,一个东西的 “钱途” 决定了我们的选择。在这种情况下,不妨试着去做一些事情,了解一些东西,因为喜欢和爱好不会凭空产生。等到你接触了足够多的事物之后,可能你会有和当初不一样的思考。总之,空想,空抱怨肯定是不行的,你需要积极地行动起来。
-
放下你的自尊
在学习一项新的技能的过程中,遭遇挫折是在所难免的。有些时候,为了学好一项技能,我们甚至都要改变之前存在多年的习惯。我们的自尊心很多时候是不接受批评,自尊心也会让我们认识到我们已经足够好了,不需要学习更多的东西。但这恰恰就是问题所在,不要忘了,你不知道你不知道的东西。如果不能做到放下自尊心,虚心地学习,那么到头来只能是井底之蛙。
往往你学的越多,会发现自己知道的越少,更加需要学习。
-
学习规则,为了能够知道如何打破他们
学习有三个阶段,从低到高分别是:学习规则,学习策略,学习如何打破策略。这里我就拿编程来举例,首先学习编程我们需要知道如何让程序运行起来,如何用编程解决实际的问题,用的多了,你就会熟悉很多规则,比如 HTTP 头是如何定义的,TCP 是怎么和 HTTP 进行连接的,操作系统线程池是怎样工作的,面向对象编程是怎么玩的。。。
学了这些就结束了吗?其实这只是第一阶段,这一阶段我们需要去了解很多我们并不熟悉的东西。在刚开始的时候,我们都会有一种知其然,不知其所以然的感觉。在这一阶段,我们可以熟练的运用某个工具或者是技术帮助我们解决问题,但我们其实并不了解这个工具或者是技术为什么要这么设计
第二阶段就是要学习一些思想层面的东西,比如怎么样的架构设计才能让程序运行的更加稳定,网络传输为什么要设计成这样的格式,操作系统的设计解决了哪些问题。在这个阶段,你会更加着重于思考 “为什么”,而不是 “是什么”。在这一阶段时间长了,你会更加了解一些技术的背景,和知识的来龙去脉。这样有助于把你之前学的东西串起来,形成一个连贯的整体。但这还不是主要的,主要的是,你能够站在技术设计者的立场和背景去思考问题。
第三阶段就是要打破策略,这一阶段可以说是最具有产出和影响力的阶段。当你在某一领域的积累和阅历到了一定程度的时候,你或许能够看到大多数人看不到的问题。如果你尝试着去解决,到最后你会设计出一个更好的技术,推动着这个领域不断向前。当然,能到这一阶段的人也是寥寥无几,有时甚至还需要有一定的运气。
不管怎样,明白你当前所在的阶段,知道你当前的目标才是最重要的。这个问题的答案只有你自己才知道。
-
实践胜过理论
有些时候,我们认为自己已经掌握了某项技术或者是能力。但是不真正的实践一番是很难发现问题的,因此就会产生 “想象中的自己” 和 “现实中的自己”。YouTube 上的这个 视频 就形象地说明了这个问题。
千万不要觉得自己会了就可以很好地应用,努力了就一定有回报。要做好失败的准备。但很多时候,失败并不是一件坏事,失败意味着这中间仍旧存在问题,我们需要做的是找到问题并想办法解决才能变得更好。受了多年应试教育的我们,可能潜意识里会觉得在测试当中犯错导致得分低是不好的。但是在生活中,犯错才能为你学习提供很好的机会和方向。
想掌握好一门技艺,刻意联系,有反馈地重复才是最关键的,这里没有捷径。
-
学习一些原则性的东西
这里列出来了一些需要知道的认知层面的东西,这些东西适用于所有的职业和领域的学习:
- 概率思维:学会如何在信息不足的情况下做好决策
- 成本效益分析
- 实验的方法
- 次级效应
- 博弈论
- 心理学
- 逻辑学
-
在过程中衡量进步,而不是看结果
如果是我们仅仅用最后的结果来衡量我们所做的事情,那我们就会陷入到误区中。其实很多时候,结果并不一定能反应我们的能力。就好比创业,大部分初创公司到最后都没能上市,那你能说没有成功的创业公司的经验都不值得借鉴吗?
另外,突然而来的成功也有可能让你停下前进的脚步。如果对待任何事情,我们能有一个初学者的心态,那是最好不过的。
Tip
这周使用了 Ant Design Pro,它是阿里旗下的蚂蚁金服开发的基于 Ant Design UI 的一个布局设计框架。给我的感觉是,这个框架节省了很多前端开发的时间。对于我这种刚接触前端不久的人来说,但又想快速做出一个像样的页面,这个框架再适合不过了,这里简单记录一下安装、使用以及介绍一下核心模块。
-
安装
>$ npm create umi安装的过程中,你可以选择 4.0 和 5.0 两个版本。其中 5.0 版本仅仅支持 TypeScript。在 4.0 版本下,你可以选择 JavaScript 或者是 TypeScript,由于对 TypeScript 不太熟,所以我选择 4.0 下的 JavaScript。
安装完成后,就是常规地,安装对应依赖:
>$ npm install -
启动
>$ npm start启动后,在本地的 8000 端口就可以看到前端页面
除了 Ant Design 之外,Ant Design Pro 主要是基于 UmiJS 和 Dva 这两个前端框架。其中 UmiJS 是一个前端的路由框架。在其官网的介绍中可以看出,这个框架其实是一个 React 工具集,就是将我们平时用的一些包,比如 react-router,react-router-dom 进行打包整理,提供一个更加人性化的结构,便于快速上手。Dva 主要是基于 redux的一个轻量级的前端框架。
UmiJS 和 Dva 在阿里内部被广泛使用,其功能是被验证过的,所以可以放心使用。
从几天的接触和使用中来看,感觉有一个框架确实省时省力了不少。但是仅仅是依附于框架解决问题是远远不够的,当前看来,还有两件事情需要去做:TypeScript 和 剖析 Ant Design Pro 的源码设计和文件架构。
Share
这次分享的是两篇关于职场焦虑的两篇文章
往往现实与我们期望的不相符,我们就会产生焦虑。此时你可以做的有两种选择,接受现实,承认自己就是当下这样一个状态,不对生活抱有更高的期待,也就不存在焦虑了。相反,另一个选择就是行动起来,有焦虑说明有问题没有解决,那么就可以根据自身的情况调整自己的短期目标。总之,最后的结果如何先不去关心,努力让自己沉浸到做事情本身上来,而不去过多思考做这件事到底能给自己带来什么样的好处。这样有助于把自己的注意力放到更有意义的事情上去,而不是毫无根据地拿自己和别人进行比较。