我有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
}
...
]
答案 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的任何累积组中。如果有,那么我只需将from
和to
值添加(或重新添加)到该组。如果尚未对from
和to
值进行分组,那么我会创建一个新组并向其添加from
和to
值。请注意,这些“组”实际上是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);