将顶点和边分组以在 JavaScript 中分隔图形

Group vertices and edges to separate graphs in JavaScript

这个问题有一个任意的有向边数组,例如,

let pairs = [[9, 6], [10, 2], [2, 1], [7, 1], [5, 6], [1, 10], [8, 11], [10, 4], [9, 0], [12, 0], [3, 9], [0, 6]];

数组一次可以包含一张或多张图表。任务是遍历这些对并将它们分成组,在这种特殊情况下,结果应该是

let output = [

    [0, 3, 5, 6, 9, 12],
    [1, 2, 4, 7, 10],
    [8, 11] 

]; //please 

let pairs = [[9, 6], [10, 2], [2, 1], [7, 1], [5, 6], [1, 10], [8, 11], [10, 4], [9, 0], [12, 0], [3, 9], [0, 6]];

let groups = [];

for(let i = 0; i < pairs.length; i++){

        if(groups.length == 0){
    
            groups.push([]);
        groups[0].push(pairs[i][0]);
        groups[0].push(pairs[i][1]);
    
    }
    else{

                let parentGroup = getParentGroup(groups, pairs[i]);
        
        if(parentGroup == null){
        
                groups.push([]);
                groups[groups.length - 1].push(pairs[i][0]);
                groups[groups.length - 1].push(pairs[i][1]);
        
        }
        else{
        
                if(parentHasNode(groups[parentGroup], pairs[i][0])){ if(!parentHasNode(groups[parentGroup], pairs[i][1])) { groups[parentGroup].push(pairs[i][1]); } }
            if(parentHasNode(groups[parentGroup], pairs[i][1])){ if(!parentHasNode(groups[parentGroup], pairs[i][0])) { groups[parentGroup].push(pairs[i][0]); } }
        
        }
                
    
    }


}

console.log(groups);

function parentHasNode(group, left){

        for(let i = 0; i < group.length; i++){
    
            if(group[i] == left) { return true; }
    
    }
    
    return false;

}

function getParentGroup(groups, pair){

        let out = null;
    
    for(let i = 0; i < groups.length; i++){
    
            for(let j = 0; j < groups[i].length; j++){
        
                if(pair[0] == groups[i][j] || pair[1] == groups[i][j]) { out = i; break; }
        
        }
    
    }
    
    return out;

}

忽略排序

这些顶点在视觉上排列成如下所示的三个图形

到目前为止,我不需要算法来绘制这些图,而是将顶点分组为单独的顶点。

我附上了当前的代码,但并不是每次都能正常工作。

let pairs = [[25, 15], [22, 6], [6, 15], [15, 9], [9, 6], [16, 9], [7, 29], [2, 5], [26, 24], [24, 29], [5, 24], [29, 5], [1, 18], [13, 12], [19, 30], [30, 18], [12, 30], [18, 12], [8, 20], [11, 3], [28, 23], [20, 3], [3, 23], [23, 20], [4, 10], [14, 27], [21, 17], [10, 17], [17, 27], [27, 10], [0, 31]];

不正确

let output =     [
    [25, 15], 
    [22, 6, 15, 9, 16], 
    [7, 29], 
    [2, 5], 
    [26, 24, 29, 5], 
    [1, 18], 
    [13, 12], 
    [19, 30, 18, 12], 
    [8, 20], 
    [11, 3, 20], 
    [28, 23, 3, 20], 
    [4, 10], 
    [14, 27], 
    [21, 17, 10, 27], 
    [0, 31]
];

第一组有3个和第6个等错误。 一定是这样的

output =   [

    [25, 15, 22, 6, 9, 16],
    [26, 24, 29, 5, 2, 7],
    [19, 30, 18, 12, 1, 13], 
    [28, 23, 3, 20, 8, 11],
    [21, 17, 10, 27, 4, 14], 
    [0, 31]

  ];

由于 pairs 数组表示有向图,因此建立组涉及遍历数组以寻找匹配对链。

下面提出的算法有一个建立当前边对的外循环,在边数组的剩余部分中寻找匹配项,如果找到任何匹配项,则将这些匹配项合并到当前边对中,当然, 正在演化为一个边集。然后清除找到的匹配项,因为它们已与当前边集合并,并且一直持续到外循环到达边数组的末尾。

最终得到的数组是合并集和清除集的组合,所以结果returns只有非空集。

function groupEdges( edges ) {

  // Turn the edge pairs into Set objects.
  temp = edges.map( po => new Set( po ) );
 
  // Loop through each edge set...
  for ( let i = 0; i < temp.length; i++ ) {
    let currentEdgeSet = temp[ i ];
    if ( currentEdgeSet ) {
      // Loop through each edge...
      let a = Array.from( currentEdgeSet );
      for ( let pi = 0; pi < a.length; pi++ ) {
        // ...and now loop through the remaining edges, seeking a match with
        // the current edge.  Include any found matching edges in with the
        // currentEdgeSet, clear the found matches (as they have been included
        // with the currentEdgeSet), and then re-establish the list of edges
        // being sought from the currentEdgeSet.
        for ( let j = i + 1; j < temp.length; j++ ) {
          if ( temp[ j ] && temp[j].has( a[ pi ] ) ) {
            temp[ j ].forEach( currentEdgeSet.add, currentEdgeSet );
            temp[ j ] = null;
            a = Array.from( currentEdgeSet );
          }
        }
      }
    }
  }
  
  // Finally, only return the non-null edge sets.
  return temp.filter( es => es );
  
}

console.log( groupEdges( [[9, 6], [10, 2], [2, 1], [7, 1], [5, 6], [1, 10], [8, 11], [10, 4], [9, 0], [12, 0], [3, 9], [0, 6]] ).map( s => Array.from( s ) ) );
console.log( groupEdges( [[25, 15], [22, 6], [6, 15], [15, 9], [9, 6], [16, 9], [7, 29], [2, 5], [26, 24], [24, 29], [5, 24], [29, 5], [1, 18], [13, 12], [19, 30], [30, 18], [12, 30], [18, 12], [8, 20], [11, 3], [28, 23], [20, 3], [3, 23], [23, 20], [4, 10], [14, 27], [21, 17], [10, 17], [17, 27], [27, 10], [0, 31]] ).map( s => Array.from( s ) ) );

请注意与遍历 currentEdgeSet 中的值相关的细微差别。 JavaScript 保留添加到 Set 对象的条目的顺序,因此能够使用索引 pi 进入 currentEdgeSet,以及有序边列表(即,按进入集合)通过 a = Array.from( currentEdgeSet ) 重新建立,只要任何新的匹配被合并到 currentEdgeSet.