图的同构识别算法——C++代码实现

图的同构识别:

给定的两个邻接矩阵,判断其三个必要非充分条件:
①结点数目相同
②变数相同
③度数相同的结点数相同
以①②③为前提进行矩阵变换,看给定的两个矩阵中,其中的一个矩阵是否能变换为另一个矩阵;

阅读文章前需要知道一个概念:
邻接矩阵中的结点的次序是有实际意义的,当结点进行行变换的时候,必须对其对应的列也进行变换
温馨提示:
博客前两片示例代码段是有小瑕疵的

实现代码和说明:

#include<iostream>
#include<stdlib.h>
#define MAX 100 
using namespace std;
 
 
struct AdjacencyMatrix{
   //邻接矩阵
 
    int points;             //邻接矩阵的顶点个数(即矩阵阶数)
    int edges;              //邻接矩阵的边的条数(即邻接矩阵非零点个数/2)
    int Matrix[MAX][MAX];   //矩阵
    int weight[MAX];        //行和度数的集合
};
 
AdjacencyMatrix A,B;//定义邻接矩阵A、B,将A调整成B且满足同构的必要条件则A、B同构
 //三个必要条件 ① 结点数相同 ②边数相同 ③ 度数相同的结点数相同 

// (行进行交换)
//行位置交换函数,返回true为正常交换,这里的行列交换都是针对于图A的 
bool SwapRows(int i,int j){
   
    int k;
    //进行 行交换
    for(k=0;k<A.points;k++){
   //矩阵的i 行和j行进行交换 
        int temp;
        temp = A.Matrix[i][k];
        A.Matrix[i][k]= A.Matrix[j][k];
        A.Matrix[j][k]= temp;
    }
    int temp;
    //行交换了,度也要跟着交换
	//这种操作,相当于把点进行移动,移动到某个位置,
	//相当于三维世界的直接拖动,即:他的点本来是堆叠起来的(或者次序不当),然后我们将他的点散开(或者移动),重新按照某种规则进行摆放(这种规则让当前图的结构(矩阵)趋近于目标图)
 //行交换完毕后其度也要记得改变 
    temp =A.weight[i];
    A.weight[i]= A.weight[j];
    A.weight[j]= temp; 
    return true;
} 
 
 //(列交换)
 
//列位置交换函数,返回true为正常交换,false为无法交换
bool  SwapColumns(int currentLayer,int i,int j){
   //为什么三个参数呢 : 为了保持前面修改的趋势 不被改变 
    int k;
    //判断是否能交换
    
    //这个循环的意思是,我之前从第一行开始,我们的图A尽量与图B相同,然后,currentLayer是当前的层次
	//可以理解为同步到当前层次了,然后,如果我们的列  交换,如果它们不相同,则 会破坏我们之前 尽量 与 图B 结构 靠近 的这个趋势的话,我们是不能让它继续进行下去的
	//因为如果我们 前面  图A和图B同步了第一行,然后图A在与图B的其他行进行 同步的时候,发现,如果交换的结果会影响到之前的同步结果的话
	//那么这样就没法同构了,也就是这两个矩阵,不可能相同 
    for(k=0;k<currentLayer;k++){
   //第i列和第j列进行调换
	 
        if(A.Matrix[k][i]!=A.Matrix[k][j]){
   
            //无法交换,因为交换后会影响先前调整的结果,故而不同构
            return false; 
        }
    }
    //进行列交换
    for(k=0;k<A.points;k++){
   
        int temp;
        temp =A.Matrix[k][i];
        A.Matrix[k][i]= A.Matrix[k][j];
        A.Matrix[k][j]= temp;
    } 
    return true;
} 


 
//用于快速排序的比较算法
int cmp( const void *a , const void *b ){
    
    return *(int *)a - *(int *)b; 
}
 
int main(){
   
    cout<<"请输入两个图的阶数(顶点数):"<<endl;
    cin>>A.points>>B.points;
    
    //判断第一个必要条件
    
	if(A.points!=B.points){
   
        cout<<"阶数不同!不同构!"<<endl;
        return 0;
    }
    
    cout<<"请输入第1个图的邻接矩阵:"<<endl;
    A.edges = 0;
    B.edges = 0;
    
    //用邻接矩阵方式输入A、B矩阵
    int i,j,k,y;
    for(i=0;i<A.points;i++){
   
        for(j=0;j<A.points;j++){
   
            cin>>A.Matrix[i][j];
            if(A.Matrix[i][j]==1){
   
                A.edges++;
            }   
        }
    }
    
    cout<<"请输入第2个图的邻接矩阵:"<<endl;
    for(i=0;i<B.points;i++){
   
        for(j=0;j<B.points;j++){
   
            cin>>B.Matrix[i][j];
            if(B.Matrix[i][j]==1){
   
                B.edges++;
            }   
        }
    }
    
    //判断第二个必要条件
    
	
	if(A.edges!=B.edges){
   
        cout<<"边的条数不同!不同构!"<<endl;
        return 0;   
    } 
    
    //因为是邻接矩阵,所以边的条数(即邻接矩阵非零点个数/2)
    //在给边进行赋值的时候,我们在二维 矩阵的值是1的时候都给边+1了,因为是无向图,G[i][j]和G[j][i] 是一样的,因此要/2 
    A.edges =A.edges/2;
    B.edges =B.edges/2;
    int Aweight[MAX];//MAX==100 
    int Bweight[MAX];
    
	
	
	//判断第三个必要条件
    int x=0;
    for(k=0;k<A.points;k++){
   //A图 共有 point 个点,然后对这些点的度数进行计算 
        int count=0;//初始化度为0 
        for(y=0;y<A.points;y++){
   
            if(A.Matrix[k][y]==1){
    //有边,度+1,这里不用考虑 要/2 ,因为是针对当前点k而言的,A.Matrix[k][y]==1就说明 k对于y点(变点)而言有边 
                count++;//度+1 
            }
        }
        Aweight[x]= count;//遍历完说有点,统计度后,将其记录在一个一维数组中 
        A.weight[x++]=count;//当然,需要将当前的度记录在A 数据结构中的 weight数组中,然后x+1;
		 
    } 
    qsort(Aweight,A.points,sizeof(Aweight[0]),cmp);
	//调用系统快速排序算法
    //进行排序的意义是: 因为 第一个点的度是不确定的,因此,我们值能将这个数组进行从小到大(或者从大到小)进行排序,排序完后,数组就是有规律的了
	//然后将 B图 记录 点度数的数组也进行从小到大(或者从大到小)进行排序,排序完后,看是否满足 :
	//同构图的三个必要条件中的第三个条件:度数相同的节点个数相同
	 
    x=0;
    //对矩阵B也进行相同的操作 
    for(k=0;k<B.points;k++){
   
        int count=0;
        for(y=0;y<B.points;y++){
   
            if(B.Matrix[k][y]==1){
   
                count++;
            }
        }
        Bweight[x]= count;
        B.weight[x++]=count;
    }
    qsort(Bweight,B.points,sizeof(Bweight[0]),cmp);//调用系统快速排序算法
    //判断是否满足第三个条件 
    for(k=0;k<A.points;k++){
   
        if(Aweight[k]!=Bweight[k]){
   
            cout<<"边的度数不同!不同构!"<<endl;
            return 0;
        }
    }
	
	
	
	//进行矩阵变换 
    
    //三个条件都满足,则进行最后的验证操作:将第一个图的矩阵进行变换,让其结构趋近于第二个图
	//并且如果操作过程没有被因为   行列交换操作 判断出错而打断(就是不能行列交换,如何行列交换都无法变换成第二个图,进而被打断)
	 
    //调整A矩阵成B  请注意:以下操作 列交换 必定伴随着 行的交换 为什么呢: 因为,虽然矩阵的行和列 之间没有太大的关联,即便行交换和列交换并不会改变其点之间的映射关系
	//也没有说 行交换后列必须得交换,但是,在表示图的矩阵中,点的次序是有含义的; 
    for(i=0;i<B.points;i++){
   
        for(j=i;j<A.points;j++){
   
            //找到度相同
            //对度数相同的结点进行行交换 
            
            
            if(B.weight[i] == A
  • 26
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是牛大春呀

老板糊涂啊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值