在自定义数据结构上查找JavaScript中两个节点之间的路径

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在自定义数据结构上查找JavaScript中两个节点之间的路径相关的知识,希望对你有一定的参考价值。

嗨我有一个连接数组如下:

var connections =[

    {
      "source": "l1",
      "target": "l2"
    },
    {
      "source": "l2",
      "target": "l4"
    },
    {
      "source": "l2",
      "target": "l3"
    },
    {
      "source": "l4",
      "target": "l5"
    },   

]

它继续源和目标。现在想要使用某个函数找到两个节点之间的路径。让我们说函数findConnections("l2", "l5")将返回如下所示的数组

var answer =[

        {
          "source": "l2",
          "target": "l4"
        },
        {
          "source": "l4",
          "target": "l5"
        }, 
]

我不知道我怎么能做到这一点?我尝试过简单的javascript但失败了。我认为使用underscore.js或lodash.js我们可以实现这个目标吗?如果有人提供解决方案或给出提示,那将会非常有用吗?

答案

使用递归并走“列表”

function find(list, from, to) {
    // find the current "source" 
    let current = list.find(v => v.source === from);

    if (!current) // no current? u got problems
        throw new Error("No from in list");

    // are we there yet?
    if (current.target === to)
        return current;

    // no we're not ... so keep searching with new "source"
    return [current].concat(find(list, current.target, to));
}
另一答案

我参加派对有点晚了,但这是一个简单的解决方案:

  1. 从给定边构建图形: graph
  2. 使用简单的breadth-first-search来寻找路径

const addNode = (graph, node) => {
  graph.set(node, {in: new Set(), out: new Set()});
};

const connectNodes = (graph, source, target) => {
  graph.get(source).out.add(target);
  graph.get(target).in.add(source);
};

const buildGraphFromEdges = (edges) => edges.reduce(
  (graph, {source, target}) => {
    if (!graph.has(source)) {
      addNode(graph, source);
    }

    if (!graph.has(target)) {
      addNode(graph, target);
    }

    connectNodes(graph, source, target);

    return graph;
  },
  new Map()
);

const buildPath = (target, path) => {
  const result = [];

  while (path.has(target)) {
    const source = path.get(target);
    result.push({source, target});
    target = source;
  }

  return result.reverse();
};

const findPath = (source, target, graph) => {
  if (!graph.has(source)) {
    throw new Error('Unknown source.');
  }

  if (!graph.has(target)) {
    throw new Error('Unknown target.');
  }

  const queue = [source];
  const visited = new Set();
  const path = new Map();

  while (queue.length > 0) {
    const start = queue.shift();

    if (start === target) {
      return buildPath(start, path);
    }

    for (const next of graph.get(start).out) {
      if (visited.has(next)) {
        continue;
      }

      if (!queue.includes(next)) {
        path.set(next, start);
        queue.push(next);
      }
    }

    visited.add(start);
  }

  return null;
};

//  A --* B
//      / | 
//     *  |  *
//    C   |   D --* E
//       |  /     *
//      * * *     /
//        F------/

const graph = buildGraphFromEdges([
  { source: 'A', target: 'B' },
  { source: 'B', target: 'C' },
  { source: 'B', target: 'D' },
  { source: 'B', target: 'F' },
  { source: 'C', target: 'F' },
  { source: 'D', target: 'E' },
  { source: 'D', target: 'F' },
  { source: 'F', target: 'E' },
]);

console.log('A -> A', findPath('A', 'A', graph)); // []
console.log('A -> F', findPath('A', 'F', graph)); // [{source: 'A', target: 'B'}, {source: 'B', target: 'F'}]
console.log('A -> E', findPath('A', 'E', graph)); // [{source: 'A', target: 'B'}, {source: 'B', target: 'D'}, {source: 'D', target: 'E'}]
console.log('E -> A', findPath('E', 'A', graph)); // null
另一答案

以下将返回JSON对象,如问题中指定的那样。它还将检查两个参数以及它们之间的连接是否存在于JSON中。我将把它留给作者来测试其他边缘情况。

var findConnections = function(json,from,to) {

  // JSON is an unordered structure, so let's compile two arrays for ease of searching
  var sources = [], targets = [];
  for ( node of json ) {
    sources.push(node.source);
    targets.push(node.target);
  }

  // Select each 'from' element from the 'sources' array, get its target...
  for(var i=0;i<sources.length;i++) {
    if( (fromIndex = sources.indexOf(from,i)) < 0 ) { throw new Error('Connection could not be established.'); }
    var fromTarget = targets[fromIndex];

    // ...then look for the connected 'to' in the 'targets' array
    for(var j=0;j<sources.length;j++) {
      if( (toIndex = sources.indexOf(fromTarget,j)) < 0 ) { continue; }

      // If we have a match, compile and return a JSON object. If not, we'll keep looping and if we loop out, an error will be thrown below
      if( targets[toIndex] == to ) {
        return [
                {
                  'source':sources[fromIndex],
                  'target':targets[fromIndex]
                },
                {
                  'source':sources[toIndex],
                  'target':targets[toIndex]                
                }
                ];
      }          
    }
  }

  throw new Error('Connection could not be established.');
}
另一答案

我花了太多时间在这上面,我很确定有一个更优雅的解决方案,但我会看看我是否可以添加评论并解释自己。

// Add some more connections. Make some of them out
// of order just for fun
let connections = [{
    source: 'l1',
    target: 'l2'
  },
  {
    source: 'l1',
    target: 'l3'
  },
  {
    source: 'l2',
    target: 'l4'
  },
  {
    source: 'l2',
    target: 'l3'
  },
  {
    source: 'l4',
    target: 'l5'
  },
  {
    source: 'l3',
    target: 'l6'
  },
  {
    source: 'l3',
    target: 'l5'
  },
  {
    source: 'l4',
    target: 'l6'
  }
];

// I just realized that I didn't call this
// function what you wanted it called but
// it should be fine

let answers = findPaths(connections, 'l1', 'l5');
console.log(JSON.stringify(answers, null, 2));

function findPaths(connections, start, end) {
  // first, build a tree, for loads of connections,
  // this is probably slow as hell
  let tree = buildTree(
    connections,
    findConnection(connections, start),
    null
  );
  
  // make the tree into all the different paths
  // also probably slow as hell. Could prune the
  // tree if I could figure out how to implement
  // a backtracking algoritm in javascript but 
  // I couldn't figure it out from the wikipedia
  // article so I skipped it
  let allPaths = buildPaths(tree, []);
  
  // pare down the paths to ones that fit the start
  // and end points
  let goodPaths = verifyPaths(allPaths, start, end);
  
  // reformat that thing to match what you wanted
  return format(goodPaths);
}

// so this thing just runs through the connections
// array and returns an array of elements where the
// source property matches the source argument
function findConnection(connections, source) {
  return connections.filter(conn => conn.source === source);
}

// So this returns an array that contains a tree
// Probably a better way to do this, but...
function buildTree(connections, nodes, parent) {
  // for each node...
  return nodes.map(node => {
    // had to cheat here, just add the node's parent
    // to the node. That way, we don't have to think
    // about it later
    node.parent = parent;
    
    // find any other connections whose source property
    // matches our target property.
    node.path = findConnection(connections, node.target);
    
    // if some were found, then...
    if (node.path && node.path.length > 0) {
      // build that sub-tree. Here, I cheated big-time
      // and made the third param (parent) an object
      // that had the properties I wanted later.
      // It's just easier.
      buildTree(connections, node.path, {
        source: node.source,
        target: node.target,
        parent: node.parent
      });
    }
    // done slapping this around, return it
    return node;
  });
}

// so this is a part could be better. Probably could be
// replaced by A

以上是关于在自定义数据结构上查找JavaScript中两个节点之间的路径的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server:查找学生在自定义日期内的连续缺勤计数

1024程序员节 | 我在自研数据库,我为技术代言

在自定义 AUAudioUnit 中查找爆裂声和爆裂声的原因

如何在自定义链表实现中找到最常见的元素?

在 SML 中查找 2-3 树中的节点数

在自定义操作之前安装组件