背景:我正在准备一些工作面试,并重温一些计算机科学基础知识。我正在实现几种遍历二进制排序树的算法,特别是按顺序遍历,并且使用同步函数,promise和async / await关键字就可以做到这一点。
对于另一套实践,我认为将其移植到异步回调函数将是一个有用的挑战,特别是因为我正在采访Node.js职位。但是我绝对会坚持下去,尽管我最好继续研究其他主题,但这种失败一直困扰着我,我一直在转动方向盘!
因此,这是对经典算法问题的一种有趣而具有挑战性的转折,而不是出于胆小!
数据因此,假设您获得了一组对象,每个对象代表一个节点。一个节点必须具有value
以及可选的left
和right
属性,这些属性与数组中另一个节点的索引号相对应。像这样:
[
{"value":"root","left":1,"right":2},
{"value":"L1","right":3},
{"value":"R1"},
{"value":"R2"}
]
如果将其绘制为二叉树,则它看起来像:
root
/ \
/ \
L1 R1
\
\
R2
并实现顺序遍历,这些值应显示为“ L1,R2,根,R1”。
在同步解决方案中,我编写了一些辅助方法来获取根节点(假设数组的第一个元素始终是根),并编写了另一种方法来在特定索引处查找节点。解决方法如下:
function SyncTree(data) {
this.data = data;
};
SyncTree.prototype.getRoot = function () {
return this.data[0];
};
SyncTree.prototype.getNode = function (index) {
return this.data[index];
};
SyncTree.prototype.orderTree = function () {
const results = [];
const inorder = node => {
if (node) {
inorder(this.getNode(node.left));
results.push(node.value);
inorder(this.getNode(node.right));
}
}
inorder(this.getRoot());
return results;
}
对于基于promise的解决方案,我使我的助手函数返回一个promise对象,而不只是返回值,而且由于async / await使得代码无论如何看起来都是同步的,所以我能够轻松地处理它。
function PromiseTree(data) {
if (!Array.isArray(data)) {
throw new Error('The data must be an Array');
}
this.data = data;
};
PromiseTree.prototype.getRoot = function (callback) {
return Promise.resolve(this.data[0]);
};
PromiseTree.prototype.getNode = function (index, callback) {
return Promise.resolve(this.data[index]);
};
PromiseTree.prototype.orderTree = function () {
const results = [];
const inorder = async node => {
if (node) {
const left = await this.getNode(node.left);
await inorder(left);
results.push(node.value);
const right = await this.getNode(node.right);
await inorder(right);
}
}
return this.getRoot().then(async root => {
await inorder(root);
return Promise.resolve(results);
});
}
对于回调挑战,我写了一些简单的逻辑,使它立即返回值或随机延迟。我知道,在现实世界中,在此用例中使用回调函数将绝对受阻,并且首选同步方法,但是我将头撞在墙上,被卡住了。到目前为止,这就是我所能得到的,但是它返回不一致的结果,因为某些回调函数先于其他回调函数发出。
function CallbackTree(data) {
if (!Array.isArray(data)) {
throw new Error('The data must be an Array');
}
this.data = data;
};
function inconsistentCallback(value, callback) {
if (Math.random() > 0.5) {
setImmediate(function () {
callback(null, value);
});
} else {
callback(null, value);
}
}
CallbackTree.prototype.getRoot = function (callback) {
inconsistentCallback(this.data[0], callback);
};
CallbackTree.prototype.getNode = function (index, callback) {
inconsistentCallback(this.data[index], callback);
};
CallbackTree.prototype.orderTree = function (callback) {
const results = [];
const inorder = (node, cb) => {
if (node) {
results.push(node.value)
this.getNode(node.left, (err, left) => {
this.getNode(node.right, (err, right) => {
inorder(node.left, cb)
inorder(node.right, cb)
cb()
})
})
}
}
this.getRoot((err, root) => {
inorder(root, () => {
if (results.length === this.data.length) {
callback(null, results)
}
});
});
}