Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARTS 第十九周(2019.11.04~2019.11.10) #19

Open
catcuts opened this issue Nov 18, 2019 · 0 comments
Open

ARTS 第十九周(2019.11.04~2019.11.10) #19

catcuts opened this issue Nov 18, 2019 · 0 comments
Labels

Comments

@catcuts
Copy link
Owner

catcuts commented Nov 18, 2019

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 太多,破坏了业务表达的流畅性)

解决第一个问题的方法是“能力下沉”;而解决代码业务表达能力缺失的问题,可以通过引入“领域(对象)模型”来解决。

综上所述,作者提出的写好复杂业务代码的方法论就是“即自上而下的结构化分解 + 自下而上的面向对象分析”。

其实看的一知半解,这里只是做一个很不严谨的作者观点提炼,便于以后实践参考。

@catcuts catcuts added the ARTS label Nov 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant