-
Notifications
You must be signed in to change notification settings - Fork 0
Description
ARTS 第三十二周(2019.11.04~2019.11.10)
Algorithm 二叉树的锯齿形层次遍历(中等难度)
题目
代码
/**
* Definition for a binary tree root.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number[][]}
*/
var zigzagLevelOrder = function(root, receiver, level = 1, rightToLeft = true) {
// 若为空树则直接返回空数组
if (!root) return receiver || [];
// 否则先初始化
receiver = receiver || [[root.val]];
// 然后取出左右节点,并计算出下一层的层序
// 这个层序对应数组的下标,该层的所有节点数值都会被放入该下标对应的元素数组中
// 从而生成最终结果
let { left, right } = root, nextLevel = level + 1;
// 若有左节点
if (left) {
// 则先将左节点的值放入对应数组
if (receiver[level]) {
// 如果是从右往左则放在数组首位
if (rightToLeft) receiver[level].unshift(left.val);
// 否则放在数组末尾
else receiver[level].push(left.val);
}
else receiver[level] = [left.val];
zigzagLevelOrder(left, receiver, nextLevel, !rightToLeft);
}
// 若有右节点,同理
if (right) {
if (receiver[level]) {
if (rightToLeft) receiver[level].unshift(right.val);
else receiver[level].push(right.val);
}
else receiver[level] = [right.val];
zigzagLevelOrder(right, receiver, nextLevel, !rightToLeft);
}
// 最终返回结果
return receiver;
};执行结果:通过
执行用时:60 ms,在所有 javascript 提交中击败了 93.15% 的用户
内存消耗:34.1 MB,在所有 javascript 提交中击败了 26.56% 的用户
思路:
若为空树则直接返回空数组,否则先初始化
然后取出左右节点,并计算出下一层的层序
这个层序对应数组的下标,该层的所有节点数值都会被放入该下标对应的元素数组中
从而生成最终结果
其中,若有左节点,则先将左节点的值放入对应数组
再递归调用处理该左节点,将其子子孙孙层节点的值放入对应数组中
放入数组时,如果是从右往左则放在数组首位,否则放在数组末尾
若有右节点,同理
最终返回结果
对比:
与高分对比:
本代码运行 33 个测试用例花费约 60ms,平均一个测试用例约 1.82ms;
高分代码运行 33 个测试用例花费约 60ms,平均一个测试用例约 1.82ms。
思路相似代码略。
Review 使用看门狗来确保 Node.js 程序的健康状况
阅读:
Ensuring healthy Node.js program using watchdog timer
点评:
在 Node.js 程序中往往充满异步操作,有些异步操作是发送 http 请求,有些异步操作是访问数据库。
在这些异步操作中,都需要与一个远程或本地的进程进行连接,并从这个连接上获得响应。
当响应迟迟无法获得时,使用 async-await 的程序就会阻塞,从而使下一个任务无法执行。
此时一种可行的方法是重启该程序。
这种方法的一种实现是使用定时器对连接进行超时监控,也就是看门狗,
一旦任务执行超过预设时间,则退出程序,由程序外部的监控进程(如 pm2)来重启它。
一种简单的实现是使用 setTimeout,但是正如文中代码所示,
如果任务中包含同步阻塞任务,则 setTimeout 也会被阻塞,从而无法达到超时监控效果
所以可以考虑使用 setTimeout + Kubernetes + [lightship](https://github.com/gajus/lightship) 来达到进程外超时监控。
扩展阅读:
Tip 异步回调里抛出的错误
异步回调里抛出的错误,只有异步回调内部才能捕捉到,外部是捕捉不到的。
比如:(详见注释)
(async function test () {
try {
await new Promise((resolve, reject) => {
try {
setTimeout(() => {
// 这里是 setTimeout 回调里抛出的错误
// setTimeout 外部的 try-catch 是捕捉不到的
// 注释掉此句,使用 reject 即在 reject 所在 promise 外部用 .catch 或 try-catch 捕捉到
throw new Error();
// 如果使用 reject
// 只有 reject 所在的 promise 外部才能捕捉到该 reject
reject(new Error());
console.log('after rejected');
}, 1000);
} catch(e) {
console.log(`捕捉 setTimeout 回调内部的错误:${e}`);
}
}).catch(e => {
console.log(`用 .catch 捕捉 promise 内部的错误:${e}`);
});
} catch(e) {
// 已经被 .catch 捕捉到,所以错误未触及此处
console.log(`用 try-catch 捕捉 promise 内部的错误:${e}`);
}
})();Share [知乎文章] 如何写好业务代码?
分享一篇知乎文章
如何写好业务代码?
本文分享了在面对复杂业务时如何编写出好的业务代码的方法论,
并且用实际业务场景作为示例,一步步推导出这套方法论。
首先面对复杂的业务流程,最直接的方法就是“过程分解”。
过程分解的方式有很多,比如“流程引擎”,又比如“依赖数据库配置的流程处理”(有的数据库提供流程处理的配置)。
本文作者并不推荐数据库配置或流程引擎的方式来进行过程分解,因为它们带来的副作用太大。
作者认为,使用最朴素的“组合方法模式”,加以分解和抽象,就可以很好地对业务流程进行分解。
但是分解之后,虽然代码可读性和可维护性得到了提高,但是也带来了两个问题:
- 领域知识被割裂肢解:简而言之就是样板代码太多,同一个问题的实现到处都是,缺少复用性
- 代码业务表达能力缺失:因为只是进行过程分解,代码就不能显式地表达出它要实现的业务
(可能因为 if-else 太多,破坏了业务表达的流畅性)
解决第一个问题的方法是“能力下沉”;而解决代码业务表达能力缺失的问题,可以通过引入“领域(对象)模型”来解决。
综上所述,作者提出的写好复杂业务代码的方法论就是“即自上而下的结构化分解 + 自下而上的面向对象分析”。
其实看的一知半解,这里只是做一个很不严谨的作者观点提炼,便于以后实践参考。