(子)图同构算法VF2实现(2)——源代码与具体实现

上一篇关于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);
				}
			}
		}


你可能感兴趣的:(VF2算法,图同构,子图同构,VF2源码)