来源:https://juejin.cn/post/6987529814324281380
作者:高晓晨
很普通的一道题
1// 假设本地机器无法做加减乘除运算,需要通过远程请求让服务端来实现。 2// 以加法为例,现有远程API的模拟实现 3 4const addRemote = async (a, b) => new Promise(resolve => { 5 setTimeout(() => resolve(a + b), 1000) 6}); 7 8// 请实现本地的add方法,调用addRemote,能最优的实现输入数字的加法。 9async function add(...inputs) { 10 // 你的实现 11} 12 13// 请用示例验证运行结果: 14add(1, 2) 15 .then(result => { 16 console.log(result); // 3 17}); 18 19 20add(3, 5, 2) 21 .then(result => { 22 console.log(result); // 10 23}) 24
答案一 最基本的答案,如果写不出来,那大概率是通过不了了
1async function add(...args) { 2 let res = 0; 3 if (args.length <= 2) return res; 4 5 for (const item of args) { 6 res = await addRemote(res, item); 7 } 8 return res; 9}
递归版本
async function add(...args) {
let res = 0;
if (args.length === 0) return res;
if (args.length === 1) return args[0];
const a = args.pop();
const b = args.pop();
args.push(await addRemote(a, b));
return add(...args);
}
常见的问题:
答案二 有候选人的答案如下:
1// Promise链式调用版本 2async function add(...args) { 3 return args.reduce((promiseChain, item) => { 4 return promiseChain.then(res => { 5 return addRemote(res, item); 6 }); 7 }, Promise.resolve(0)); 8 9}
从这个实现可以看出:
这个版本至少能到 70 分
答案三 之前的答案结果都是对的,但是我们把耗时打出来,可以看到耗时和参数个数成线性关系,因为所有计算都是串行的,显然不是最优的解。
更好一点的答案:
1function add(...args) { 2 if (args.length <= 1) return Promise.resolve(args[0]) 3 const promiseList = [] 4 for (let i = 0; i * 2 < args.length - 1; i++) { 5 const promise = addRemote(args[i * 2], args[i * 2 + 1]) 6 promiseList.push(promise) 7 } 8 9 if (args.length % 2) { 10 const promise = Promise.resolve(args[args.length - 1]) 11 promiseList.push(promise) 12 } 13 14 return Promise.all(promiseList).then(results => add(...results)); 15} 16
可以看到很明显的优化。
答案四 还能再优化吗? 有些同学会想到加本地缓存
1const cache = {}; 2 3function addFn(a, b) { 4 const key1 = `${a}${b}`; 5 const key2 = `${b}${a}`; 6 const cacheVal = cache[key1] || cache[key2]; 7 8 if (cacheVal) return Promise.resolve(cacheVal); 9 10 return addRemote(a, b, res => { 11 cache[key1] = res; 12 cache[key2] = res; 13 return res; 14 }); 15} 16
加了缓存以后,我们再第二次执行相同参数加法时,可以不用请求远端,直接变成毫秒级返回
有些时候会让候选人将代码提交到 Github 仓库,以工作中一个实际的模块标准来开发,可以考察:
实际题目
// 有一个 10G 文件,每一行是一个时间戳,
// 现在要在一台 2C4G 的机器上对它进行排序,输出排序以后的文件
// 案例输入
// 1570593273487
// 1570593273486
// 1570593273488
// …
// 输出
// 1570593273486
// 1570593273487
// 1570593273488
// …
先看一个答案,看看哪里有问题
async function sort(inputFile, outputFile) {
const input = fs.createReadStream(inputFile);
const rl = readline.createInterface({ input });
const arr = [];
for await (const line of rl) {
const item = Number(line);
arr.push(item);
}
arr.sort((a, b) => a - b);
fs.writeFileSync(outputFile, arr.join('\n'));
}
10GB 的文件无法一次性放进内存里处理,内存只有 4GB
再看一个神奇的答案,只有一行代码,而且从结果来说是正确的。但不是我们笔试想要的答案。
const cp = require('child_process');
function sort(inputFile, outputFile) {
cp.exec(`sort -n ${inputFile} > ${outputFile}`);
}
解题思路
再将问题拆解
本题最难的点在于如何合并所有小文件。代码如何实现?
堆有一些特性:
我们尝试把下面数组构造成一个最小堆
从最后一个非叶子节点开始往前处理
10 比 5 大,所以交换它们的位置
然后是节点 2,符合要求不需要处理
最后到顶点 3,它比左子节点大,所以要交换