图形 - 如何找到最大的诱导子图H,使得H中的每个顶点的度数≥k

12

这是一个关于图的练习题。

给定一个有n个点和m条边的无向图G,以及一个整数k,设计一个O(m+n)算法,找出G的最大诱导子图H,满足H中每个顶点的度≥k,或者证明不存在这样的图。 图G的诱导子图F = (U, R) 是指由G的一个子集U构成的子图,并且包含U中所有在G中相邻的点之间的边R。

我的初始想法如下:

首先,这个练习实际上要求我们找到所有度数大于或等于k的顶点S,然后删除S中没有与其他顶点相连的顶点。然后经过筛选的S就是H,其中所有顶点的度数都≥k,它们之间的边为R。

此外,它要求O(m+n),因此我认为需要使用BFS或DFS。但我卡住了。

在BFS中,我可以知道一个顶点的度数。但是一旦我得到v(一个顶点)的度数,除了其父节点之外,我不知道其他相邻的顶点。但如果父节点的度数不≥k,则无法将v排除,因为它仍可能与其他顶点相连。

有什么提示吗?


编辑:

根据@Michael J. Barber的答案,我实现了代码并将关键方法public Graph kCore(Graph g, int k)更新在此处。请问我的实现正确吗?它是O(m+n)吗?

class EdgeNode {
   EdgeNode next;
   int y;
}

public class Graph {
   public EdgeNode[] edges;
   public int numVertices;

   public boolean directed;

   public Graph(int _numVertices, boolean _directed) {
      numVertices = _numVertices;
      directed = _directed;
      edges = new EdgeNode[numVertices];
   }

   public void insertEdge(int x, int y) {
      insertEdge(x, y, directed);
   }

   public void insertEdge(int x, int y, boolean _directed) {
      EdgeNode edge = new EdgeNode();
      edge.y = y;
      edge.next = edges[x];
      edges[x] = edge;
      if (!_directed)
          insertEdge(y, x, true);
   }

   public Graph kCore(Graph g, int k) {
      int[] degree = new int[g.numVertices];
      boolean[] deleted = new boolean[g.numVertices];
      int numDeleted = 0;
      updateAllDegree(g, degree);// get all degree info for every vertex

      for (int i = 0;i < g.numVertices;i++) {
          **if (!deleted[i] && degree[i] < k) {
           deleteVertex(p.y, deleted, g);
          }**
      }

      //Construct the kCore subgraph
      Graph h = new Graph(g.numVertices - numDeleted, false);
      for (int i = 0;i < g.numVertices;i++) {
         if (!deleted[i]) {
           EdgeNode p = g[i];
           while(p!=null) {
              if (!deleted[p.y])
                 h.insertEdge(i, p.y, true); // I just insert the good edge as directed, because i->p.y is inserted and later p.y->i will be inserted too in this loop.
                 p = p.next;
              }
           }
         }
      }

      return h;
  }

  **private void deleteVertex(int i, boolean[] deleted, Graph g) {
     deleted[i] = true;
     EdgeNode p = g[i];
     while(p!=null) {
        if (!deleted[p.y] && degree[p.y] < k) 
            deleteVertex(p.y, deleted, g);
        p = p.next;
     }
  }**

  private void updateAllDegree(Graph g, int[] degree) {
     for(int i = 0;i < g.numVertices;i++) {
         EdgeNode p = g[i];
         while(p!=null) {
            degree[i] += 1;
            p = p.next;
         }
     }
  }

}

->

}


我正在尝试使用以下方法:1)使用BFS / DFS查找所有度数> = k的顶点。现在这些顶点肯定会在诱导子图中。2)现在我们需要找出它们是否相互连接。我们可以通过每个这些顶点的邻接列表进行遍历。O(m)。总体上是O(m + n)+ O(m)= O(m + n)。算法逻辑正确吗? - Sumit Trehan
1个回答

17
一个顶点度数最小为k的极大诱导子图被称为k-core。您可以通过反复删除度数小于k的任何顶点来找到k-cores。
在实践中,首先评估所有顶点的度数,这需要O(m)时间。然后遍历顶点,查找度数小于k的顶点。当您找到这样的顶点时,请从图中切断它,并更新邻居的度数,同时删除任何度数下降到k以下的邻居。您需要至少查看每个顶点一次(因此可以在O(n)时间内完成),并且对于每条边最多只需更新度数一次(因此可以在O(m)时间内完成),总渐近界为O(m+n)。
剩余的连通分量是k-cores。通过评估它们的大小来找到最大的一个。

谢谢。我们这样做怎么样?首先我将 G 克隆到 H,即 H 在一开始与 G 完全相同。然后我进行 BFS。我选择 BFS 的原因是因为我可以计算每个顶点的度数并在一次遍历中处理边缘 (m+N)。每当我遇到一个度数小于 k 的顶点时,我只需删除它和 H 中所有相关的边缘。但是,在邻接表中删除顶点和相关边缘会很麻烦。 - Jackson Tale
@JacksonTale 你可以按照任何顺序迭代遍历顶点;BFS是可以的,但要记住图可能不连通。每当你减少一个顶点的度数时,你还需要删除任何度数低于k的顶点;你需要跟踪度数 - 重新计算度数会导致O(m^2)的时间复杂度。将初次度数计算与处理相互交错不会影响渐近性能,所以我建议分成两个步骤进行,听起来更容易。最后,你不需要复制整个图;你真正需要的只是一个度数数组。 - Michael J. Barber
如果我理解你的程序正确,你错过了删除一个顶点可能会使你已经访问过的顶点失效的可能性。每当你减少一个顶点的度数时,你需要再次进行测试。你不能只在构建子图时进行测试:否则会使其他顶点无效。 - Michael J. Barber
但是如果我那样做,时间复杂度仍然会是O(m+n)吗? - Jackson Tale
@JacksonTale 是的,那就是想法。 - Michael J. Barber
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接