javascript分组链接节点

时间:2017-05-04 21:05:18

标签: javascript arrays list d3.js grouping

我有2个列表 - 节点和链接......现在我想要的是将所有直接/间接链接元素添加到不同组中的最有效方法....例如, 0连接到1连接到2所以节点0,1,2变成组1 ....节点3连接到4所以它变成组2等等....预先感谢你的帮助:)这是我正在做的d3实现的一部分..

PS:这些列表很容易存在于数万个节点和链接中。

"nodes":[
   {
      "id":0,
      "x":1509.9862,
      "y":-609.1013
   },
   {
      "id":1,
      "x":1645.9578,
      "y":-85.06705
   },
   {
      "id":2,
      "x":1948.1533,
      "y":-469.3646
   },
   {
      "id":3,
      "x":348.1533,
      "y":-669.3646
   },
   {
      "id":4,
      "x":1448.1533,
      "y":-1469.3646
   }
   ...
  ]

  "links":[
     {
        "from":0,
        "to":1
     },
     {
        "from":1,
        "to":2
     },
     {
        "from":3,
        "to":4
     }
     ...
  ]  

3 个答案:

答案 0 :(得分:3)

这是一个典型的UnionFind问题。我们的想法是将每个节点视为一个指针指向其父节点的集合。具有相同父亲的节点属于同一组。因此,对于您的问题,我们可以在开头创建n个集合。然后遍历链接以对通过相同链接连接的每个人进行分组。复杂度为O(n),其中n是节点数。

nodes = [{
    "id": 0,
    "x": 1509.9862,
    "y": -609.1013
  },
  {
    "id": 1,
    "x": 1645.9578,
    "y": -85.06705
  },
  {
    "id": 2,
    "x": 1948.1533,
    "y": -469.3646
  },
  {
    "id": 3,
    "x": 348.1533,
    "y": -669.3646
  },
  {
    "id": 4,
    "x": 1448.1533,
    "y": -1469.3646
  }
];

links = [{
    "from": 0,
    "to": 1
  },
  {
    "from": 1,
    "to": 2
  },
  {
    "from": 3,
    "to": 4
  }
];

// union-find is a data structure that can union two sets and check
// whether two element in the same set.

var father = {};

function group(nodes, links) {
  // create n set with each set has the node as its only element
  nodes.forEach(function(node, i) {
    father[node.id] = node.id;
  });

  // union each set that has a link between them
  links.forEach(function(link, i) {
    union(link.from, link.to);
  });

  // for each unioned set, group nodes together
  var id = 1;
  var groupIdCnt = {};
  var groupIds = {};
  nodes.forEach(function(node, i) {
    var f = find(node.id);
    if (typeof groupIds[f] === 'undefined') {
      groupIds[f] = id;
      groupIdCnt[id] = 1;
      id++;
    } else {
      groupIdCnt[groupIds[f]]++;
    }
  });

  var groups = {};
  nodes.forEach(function(node, i) {
    var f = find(node.id);
    if (groupIdCnt[groupIds[f]] === 1) {
      node['group'] = 0;
    } else {
      node['group'] = groupIds[f];
    }

    if (typeof groups[node['group']] === 'undefined') {
      groups[node['group']] = [];
    }
    groups[node['group']].push(node);
  });

  return Object.values(groups);

}

// find father of each set
function find(node) {
  // if it is the root, return
  if (father[node] === node) {
    return node;
  }
  // if not, find the father and point to it
  father[node] = find(father[node]);
  return father[node];
}

// update the father of set which includes node1 to the father of set which includes node 2
function union(node1, node2) {
  father[find(node1)] = find(node2);
}

// O(n), since we visit each node once
var groups = group(nodes, links);
console.log(nodes);
console.log(groups);

答案 1 :(得分:1)

在下面的解决方案中,我正在创建link个群组,这些群组彼此相关联。我这样做是通过循环遍历所有from / to组合,并找出是否已将任何一个已添加到link s的任何累积组中。如果有,那么我只需将fromto值添加(或重新添加)到该组。如果尚未对fromto值进行分组,那么我会创建一个新组并向其添加fromto值。请注意,这些“组”实际上是Javascript集,这是一种新的ES6 / ES2015数据类型,可以更轻松地处理不需要和/或允许重复的元素“组”。

一旦建立了一组/一组链接,我就会向每个node添加一个属性,指示它所属的link组。

请注意,为了这个演示代码,我简化/解除了一些node值。我还添加了一些额外的链接,只是为了演示一些需要处理的其他案例。

const groupNodes = (nodes, links) => {
  const groups = links.reduce((grps, {from, to}) => {
    if (!grps.some(grp => {
      if (grp.has(from) || grp.has(to)) return grp.add(from).add(to);
    })) grps.push(new Set([from, to]));
    return grps;
  }, []);
  nodes.forEach(node => {
    groups.forEach((grp, i) => { if (grp.has(node.id)) node.group = i; });
  });
  return nodes;
};



const nodes = [
  {
    "id":0,
    "x":0,
    "y":0
  },
  {
    "id":1,
    "x":11,
    "y":-11
  },
  {
    "id":2,
    "x":22,
    "y":-22
  },
  {
    "id":3,
    "x":33,
    "y":-33
  },
  {
    "id":4,
    "x":44,
    "y":-44
  },
  {
    "id":5,
    "x":55,
    "y":-55
  },
  {
    "id":6,
    "x":66,
    "y":-66
  }
];
const links = [
  {
    "from": 0,
    "to"  : 1
  },
  {
    "from": 1,
    "to"  : 2
  },
  {
    "from": 2,
    "to"  : 0
  },
  {
    "from": 3,
    "to"  : 4
  },
  {
    "from": 4,
    "to"  : 5
  },
  {
    "from": 6,
    "to"  : 0
  }
];

console.log(JSON.stringify(groupNodes(nodes, links)));

答案 2 :(得分:1)

此代码吐出一个对象,其键是节点ID,其值是组ID,不一定是顺序的。

var obj = {
"links":[
     {
        "from":0,
        "to":1
     },
     {
        "from":1,
        "to":2
     },
     {
        "from":5,
        "to":4
     },
     {
        "from":3,
        "to":4
     }
  ]  
};

var groups = {};
var nextGrp = 1;

for (var i=0, l; l = obj.links[i]; i++) {
  if (groups[l.from]) {
    if (groups[l.to]) {
      if (groups[l.to] != groups[l.from]) {
        // the two items span two different groups which must now be joined into 1
        for (var j in groups) {
          if (groups[j] == groups[l.to]) {
            groups[j] = groups[l.from];
          }
        }
      }
    } else {
      groups[l.to] = groups[l.from];
    }
  } else if (groups[l.to]) {
    groups[l.from] = groups[l.to];
  } else {
    groups[l.from] = nextGrp;
    groups[l.to] = nextGrp;
    nextGrp++;
  }
}

console.log(groups);