30

我最近面试了 Facebook 的前端工程师职位。对于我的手机屏幕,我有以下问题:给定一个来自 DOM 树的节点,从相同的 DOM 树中找到位于相同位置的节点。为清楚起见,请参见下图。

 A         B

 O        O
 |\       |\
 O O      O O
  /|\      /|\
 O O O    O O O
      \        \
       O        O

这是我的解决方案,我想知道我可以做些什么来改进/优化它。

var rootA, rootB;

function findNodeB(nodeA) {
    // Variable to store path up the DOM tree
    var travelPath = [];

    // Method to travel up the DOM tree and store path to exact node
    var establishPath = function(travelNode) {
        // If we have reached the top level node we want to return
        // otherwise we travel up another level on the tree
        if (travelNode === rootA) {
            return;
        } else {
            establishPath(travelNode.parentNode);
        }

        // We store the index of current child in our path
        var index = travelNode.parentNode.childNodes.indexOf(travelNode);
        travelPath.push(index);     
    }

    var traverseTree = function(bTreeNode, path) {
        if(path.length === 0) {
            return bTreeNode;
        } else {
            traverseTree(bTreeNode.childNodes[path.pop()], path);
        }
    }

    establishPath(rootB, nodeA);

    return traverseTree(rootB, travelPath);
}           
4

4 回答 4

23

由于至少 Axel 对迭代解决方案表现出兴趣,因此它是:

给定具有相同结构的两棵树,以及第一棵树中的指定节点,找到第二棵树中具有相同位置的节点在结构中。

如果我们没有关于这两棵树的其他信息,那么每个节点的位置可以被描述为从根节点开始的路径,其中路径中的每个步骤都被指定为 childNode 数组的索引。

function indexOf(arrLike, target) {
    return Array.prototype.indexOf.call(arrLike, target);
}

// Given a node and a tree, extract the nodes path 
function getPath(root, target) {
    var current = target;
    var path = [];
    while(current !== root) {
        path.unshift(indexOf(current.parentNode.childNodes, current));
        current = current.parentNode;
    }
    return path;
}

// Given a tree and a path, let's locate a node
function locateNodeFromPath(root, path) {
    var current = root;
    for(var i = 0, len = path.length; i < len; i++) {
        current = current.childNodes[path[i]];
    }
    return current;
}

function getDoppleganger(rootA, rootB, target) {
    return locateNodeFromPath(rootB, getPath(rootA, target));
}

编辑:正如 Blue Skies 所观察到的,childNodes 没有 .indexOf()。使用 Array.prototype.indexOf.call() 更新

于 2013-11-05T00:10:24.410 回答
7

这是并行遍历解决方案

function findDomNodeInTree(rootA, rootB, targetNode) {
    if (rootA === targetNode){
        return rootB;
    }

    let nodeB = null;

    for (let i=0; i<rootA.childNodes.length && nodeB === null; i++){
        nodeB = findDomNodeInTree(rootA.childNodes[i], rootB.childNodes[i], targetNode);
    }

    return nodeB;
}

它的时间复杂度是 O(N),在最坏的情况下我们需要遍历整个树。

我不认为它比首先找到路径的解决方案效率低。在每个级别都有一个调用indexOf,可能需要遍历该级别上的所有节点才能找到索引。

于 2018-01-19T13:14:57.893 回答
3

代替Array.prototype.indexOf.call,您可以使用Array.from(在 ES6 中标准化):

Array.from(travelNode.parentNode.childNodes).indexOf(travelNode);

于 2016-12-29T01:55:52.590 回答
0

我将并行遍历两棵树,当我到达 treeA 中的节点时,返回并行节点。

于 2014-07-07T15:52:02.617 回答