上一篇关于VF2的博客写的太水了,http://blog.csdn.net/mmc2015/article/details/49777447。而且错误百出。
至于VF2思想,看原论文吧。
1)数据结构:
//每个邻接边的结构,注意,该结构不单独使用,必须和某个点一起使用 struct EDGE { int id2; //边的另一端的点的id int id2Label; //注意,两条边一样,除了该边上的label一样,还要另一端的节点的label(注意不是id,所以才加了这个变量)也一样 int label; //边上的label }; struct NODE { int id; int label; int edgeNumber; //邻接的边有多少条 vector<EDGE> adjacencyEdgeSet; //邻接的边 }; //邻接链表结构,不过是用vector来实现 struct GRAPH { int graphID; vector<NODE> nodeSet; };
2)读数据:
vector<GRAPH *> ReadGraph(const char *filename) { FILE *fp = fopen(filename, "r"); /* if (!fp) { printf("fp is NULL, file [%s] doesn't exist...\n", filename); return NULL; } */ char mark[2]; int id,label,id2; NODE node; EDGE edge; vector<GRAPH *> gSet; GRAPH * g = NULL; while(true) { fscanf(fp, "%s", mark); if(mark[0]=='t') { fscanf(fp, "%s%d", mark, &id); if(id==-1) { int i,s=g->nodeSet.size(),j,eN; for(i=0;i<s;++i) { eN=g->nodeSet[i].adjacencyEdgeSet.size(); //计算最后一个graph中每个node的邻接边的个数 g->nodeSet[i].edgeNumber=eN; for(j=0;j<eN;++j) //找到每一个id2对应的id2Label { g->nodeSet[i].adjacencyEdgeSet[j].id2Label=g->nodeSet[g->nodeSet[i].adjacencyEdgeSet[j].id2].label; } } gSet.push_back(g); //保存最后一个graph break; } else //if input not ending, then { if(g==NULL) //第一个graph { g = new GRAPH; g->graphID=id; } else //后面的graph { int i,s=g->nodeSet.size(),j,eN; for(i=0;i<s;++i) { eN=g->nodeSet[i].adjacencyEdgeSet.size(); //先计算上一个graph中每个node的邻接边的个数 g->nodeSet[i].edgeNumber=eN; for(j=0;j<eN;++j) //找到每一个id2对应的id2Label { g->nodeSet[i].adjacencyEdgeSet[j].id2Label=g->nodeSet[g->nodeSet[i].adjacencyEdgeSet[j].id2].label; } } gSet.push_back(g); //再把上一个graph保存 g = new GRAPH; g->graphID=id; } } } else if(mark[0]=='v') { fscanf(fp, "%d%d", &node.id, &node.label); g->nodeSet.push_back(node); } else if(mark[0]=='e') { fscanf(fp, "%d%d%d", &id, &id2, &label); edge.label=label; //这里假设无向边(相当于双向的有向边) edge.id2=id2; g->nodeSet[id].adjacencyEdgeSet.push_back(edge); //id->id2的边(g->nodeSet[id],利用了id和下标正好一一对应的特性:i=g->nodeSet[i].id) edge.id2=id; g->nodeSet[id2].adjacencyEdgeSet.push_back(edge); //id2->id的边 } } fclose(fp); printf("graph number:%d\n", gSet.size()); return gSet; }
3)比较简单的feasibility rules:
//其实 pair(quG_vID, dbG_vID)就是一个候选pair candidate //判断该候选pair是否满足feasibility rules bool FeasibilityRules(GRAPH *quG, GRAPH *dbG, int quG_vID, int dbG_vID) { //首先,判断quG_vID和dbG_vID对应的label是否相同 if(quG->nodeSet[quG_vID].label!=dbG->nodeSet[dbG_vID].label) //如果两个点的label不同,则【一定不】满足feasibility rules { return false; } //其次,判断quG_vID邻接边的个数是否大于dbG_vID邻接边的个数 if(quG->nodeSet[quG_vID].edgeNumber>dbG->nodeSet[dbG_vID].edgeNumber) //如果大于,quG一定不能匹配dbG,则【一定不】满足feasibility rules { return false; } //再次,判断是不是每次match的第一个比较pair if(match.quMATCHdb.size()==0) //如果是第一个比较pair { //只需要这两个点的label相同(已经判断成立了)即满足feasibility rules return true; } //最后(label相同,邻接边满足数量关系,同时不是第一个pair【即,之前已经match了一部分节点】),那么只要下面条件成立就能满足最简单的feasibility rules,即: //1)如果quG_vID与quG中的已经match的任何节点都不相邻,那么上面已经判断过的三个条件就已经保证,(从当前角度看)quG_vID和dbG_vID加入M(s)是满足feasibility rules的; //2)如果quG_vID与quG中已经match的某些节点quG_M_vID之间存在邻接边(quG_vID是quG_M_vID的“neighbor节点”), // 则必须要求dbG_vID与dbG中的节点dbG_M_vID(其中节点dbG_M_vID是与节点quG_M_vID已经match好的)之间也存在邻接边(dbG_vID是dbG_M_vID的“neighbor节点”), // 而且要求【所有的】邻接边对( edge(quG_vID,quG_M_vID), edge(dbG_vID,dbG_M_vID) )的label一样。 //下面先判断2) int i,j,quG_M_vID,dbG_M_vID,quG_vID_adjacencyEdgeSize=quG->nodeSet[quG_vID].edgeNumber,dbG_vID_adjacencyEdgeSize=dbG->nodeSet[dbG_vID].edgeNumber; for(i=0;i<quG_vID_adjacencyEdgeSize;++i) //对于节点quG_vID的每一个邻接的EDGE(注意,包括边上的label和另一端的节点的label) { quG_M_vID=quG->nodeSet[quG_vID].adjacencyEdgeSet[i].id2; //获取quG_vID邻接的第i个EDGE另一端的节点quG_M_vID if(match.quMATCHdb.count(quG_M_vID)==0) //如果节点quG_M_vID还没有match(不在状态M(s)的集合中),则不考虑 { continue; } else //节点quG_M_vID已经match上了(在状态M(s)的集合中),则必须要求邻接边对( edge(quG_vID,quG_M_vID), edge(dbG_vID,dbG_M_vID) )的label一样 { dbG_M_vID=match.quMATCHdb[quG_M_vID]; //获取和节点quG_M_vID相match的节点为dbG_M_vID for(j=0;j<dbG_vID_adjacencyEdgeSize;++j) { //如果边edge(dbG_vID,dbG_M_vID)存在 if( dbG->nodeSet[dbG_vID].adjacencyEdgeSet[j].id2==dbG_M_vID ) { //并且( edge(quG_vID,quG_M_vID), edge(dbG_vID,dbG_M_vID) )的label一样 if( quG->nodeSet[quG_vID].adjacencyEdgeSet[i].label==dbG->nodeSet[dbG_vID].adjacencyEdgeSet[j].label ) { break; //考虑下一组邻接边对( edge(quG_vID,quG_M_vID), edge(dbG_vID,dbG_M_vID) ) } else //边虽然存在,但边上的label不相同,则【一定不】满足feasibility rules { return false; } } } if(j==dbG_vID_adjacencyEdgeSize) //说明边edge(dbG_vID,dbG_M_vID)不存在(但边edge(quG_vID,quG_M_vID)存在),则【一定不】满足feasibility rules { return false; } } } //能从for循环中出来,有两种情况: //1)quG_vID与quG中的已经match的任何节点都不相邻 //2)有相邻边,并且【所有的】邻接边都满足feasibility rules return true; //这两种情况都意味着要返回true }
5)改进原文给出的方法:
<span style="white-space:pre"> </span>/* 其实quG是不需要循环每一个未匹配的node的,因为: 如果quG的node i与任何一个dbG的node都不满足feasibility rules,则整个quG就不满足要求, 如果quG的node i与某个dbG的node满足feasibility rules,则if里递归调用RecursivelyMatchGraph()会自动执行对dbG的node i+1的判断 所以这里只需要对当前quG中【第一个】未匹配的node进行匹配判断即可!!!。。。 int i,iS=quG->nodeSet.size(); for(i=0; i<iS && match.quMATCHdb.count(i)==0 ;++i) //每一个还没有match上的quG的节点(match.quMATCHdb.count(i)利用了id和下标正好一一对应的特性:i=quG->nodeSet[i].id) { int j,jS=dbG->nodeSet.size(); for(j=0; j<jS && match.dbMATCHqu.count(j)==0 ;++j) //每一个还没有match上的dbG的节点 { //compute the set P(s) of the pairs candidate for inclusion in M(s): //其实 pair(i, j)就是一个候选pair candidate if(FeasibilityRules(quG, dbG, i, j)) //if the feasibility rules succeed, then { //compute the state s' obtained by adding pair(i, j) to M(s) //call Match(s') } } } */ int i,iS=quG->nodeSet.size(); for(i=0;i<iS;++i) //每一个还没有match上的quG的节点 { if(match.quMATCHdb.count(i)==0) //(match.quMATCHdb.count(i)利用了id和下标正好一一对应的特性:i=quG->nodeSet[i].id) { break;//找到当前quG中【id从小到大排列时第一个】未匹配的node的id为i,跳出去 } } int j,jS=dbG->nodeSet.size(); for(j=0; j<jS && match.dbMATCHqu.count(j)==0 ;++j) //每一个还没有match上的dbG的节点 { //compute the set P(s) of the pairs candidate for inclusion in M(s): //pair(i, j)就是一个候选pair candidate if(FeasibilityRules(quG, dbG, i, j)) //if the feasibility rules succeed, then { //compute the state s' obtained by adding pair(i, j) to M(s) match.quMATCHdb[i]=j; match.dbMATCHqu[j]=i; //call Match(s') RecursivelyMatchGraph(quG, dbG); if(correctMatch==true) //如果quG和dbG的某些点和边已经match上了 { break; //直接结束quG和dbG的匹配过程(虽然quG还可能和dbG另外一些不同的点/边集合match上,此处只找一个,以减少搜索) } else //没match上,回滚到上一阶段 { match.quMATCHdb.erase(i); match.dbMATCHqu.erase(j); } } }