若一个无向图存在欧拉回路,则图中所有的点的度数都为奇数的点的个数为2或0.
本题中,给定顶点数vertexNum,和每个点的度数degreeNum(当然这里的度数规定为偶数),且vertexNum>degreeNum。
则,必然存在欧拉回路。
#include<stdio.h>
#define vertexNum 20
#define degreeNum 10
int upLimiteValue(int a, int b);
void Drawing(int a[][vertexNum]);
void print(int a[][vertexNum]);
//定义一个邻接矩阵,记录无向图
int matri[vertexNum][vertexNum];
//定义一个矩阵,记录每个顶点的度数
int ved[vertexNum];
void Find_Euler_circuit(int a[][vertexNum]);
void main(void)
{
int i,j;
//矩阵初始化,两个点有边为1,无边为0
for(i=0; i<vertexNum; i++)
{
ved[i]=0; /*每个顶点的度数初始化为0*/
for(j=0; j<vertexNum; j++)
{
matri[i][j]=0;
}
}
Drawing(matri);
print(matri);
Find_Euler_circuit(matri);
}
//构造一个图
void Drawing(int a[][vertexNum])
{
//先构造一棵树,思想:从起点开始,每个点依次与其他顶点相连,直到顶点度数为degreeNum则选择第二层的第一个点作为起点,连接那些还是孤立的
//点,依次类推,第三层的第一个起点,第四层的第一个起点,每次都连接那些孤立的点来满足度数,这样可以保证所有的点已经连通。
int i,j=1,k;
int h;
//求一下构造树的高度
h=2+upLimiteValue(vertexNum-(degreeNum+1),(degreeNum-1));
for(i=0; i<h; i++) //逐层构造
{
//查找每层的第一个起点的位置
if(i==0 || i==1)
{
k=i;
}
else if(i==2)
{
k=k+degreeNum;
}
else
{
k=k+degreeNum-1;
}
for(; (ved[k]<degreeNum) && (j<vertexNum); j++)
{
if(a[k][j]==0)
{
a[k][j]=1;
a[j][k]=1;
ved[k]++;
ved[j]++;
}
}
}
//把构造出的树中,所有的顶点的度数添加到degreeNum
for(i=0; i<vertexNum; i++)
{
if(ved[i] != degreeNum)
{
for(j=i+1; j<vertexNum && ved[i]!=degreeNum; j++)
{
if(ved[j] != degreeNum && a[i][j]==0)
{
a[i][j]=1;
a[j][i]=1;
ved[i]++;
ved[j]++;
}
}
}
}
//补图的过程中,可能有些点的度没有达到规定的度数,需要再进行处理,这里可以得出未达到degreeNum的度一般都在最后,我们可以从后往前遍历
for(i=vertexNum-1; ved[i]!=degreeNum; i--)
{
//因为对度数进行补全时,是从前到后,所以未补全的度只能是最后一个点或两个点
if(ved[i]%2 != 0) //假如当前度数是奇数,则前面的度数必然也是奇数
{
for(j=0; j<i && ved[i]!=degreeNum; j++)
{
for(k=j+1; k<i; k++)
{
if(a[i][j]==0 && a[i][k]==0 && a[i-1][j]==0 && a[i-1][k]==0 && a[j][k]==1)
{
a[j][k]=0;
a[k][j]=0;
a[i][j]=1;
a[j][i]=1;
a[i-1][k]=1;
a[k][i-1]=1;
ved[i]+=1;
ved[i-1]+=1;
break;
}
}
}
}
else
{
for(j=0; j<i && ved[i]!=degreeNum; j++)
{
for(k=j+1; k<i; k++)
{
if(a[i][j]==0 && a[i][k]==0 && a[j][k]==1)
{
a[j][k]=0;
a[k][j]=0;
a[i][j]=1;
a[j][i]=1;
a[i][k]=1;
a[k][i]=1;
ved[i]+=2;
break;
}
}
}
}
}
}
//寻欧拉回路的思想:从起点开始走,编号小的优先,能得出一个个的小回路,把这些回路上的路径
//按每个小回路得出来的先后次序
//进行编号,直到所有边都遍历。然后再从起点开始走,先走回路编号的路径,然后选择定点编号小的走,
//走完所有边,就能得出Euler回路
void Find_Euler_circuit(int a[][vertexNum])
{
int i,j;
int k=2; //标记回路号
int base=0;
int vNum=1;
int road[(vertexNum*degreeNum)/2+1]={0}; //记录每个小回路上的点
int start=base;
int mark=0; //标记已放入回路中的顶点号
while(vNum<=(vertexNum*degreeNum)/2)
{
if(ved[base]==0) //假如顶点的度数已满,则找下一个回路起点,回路号要+1
{
base=road[++mark];
start=base;
continue;
}
for(i=0; i<vertexNum; i++) //查找一个顶点编号最小的与其相邻的点
{
if(a[start][i]==1)
{
a[start][i]=k;
a[i][start]=k;
ved[i]--;
ved[start]--;
road[vNum]=i;
start=i;
vNum++;
break;
}
}
if(start==base) //又回到起点,则完成一个回路,
{
k++;
}
}
printf("*****************************************************\n");
print(a);
//开始寻找欧拉回路
i=0;
int bestchoice;
int num=0;
int jmark;
printf("输出欧拉回路路径如下:");
while(num<(vertexNum*degreeNum)/2)
{
bestchoice=0;
jmark=0;
for(j=0; j<vertexNum; j++)
{
if(a[i][j]>bestchoice)
{
bestchoice=a[i][j];
jmark=j;
}
}
num++;
printf("%d-->%d\t",i,jmark);
a[i][jmark]=0;
a[jmark][i]=0;
i=jmark;
}
}
//求上限值
int upLimiteValue(int a, int b)
{
if(a%b==0)
{
return a/b;
}
else
{
return a/b+1;
}
}
//打印矩阵
void print(int a[][vertexNum])
{
int i,j;
for(i=0; i<vertexNum; i++)
{
for(j=0; j<vertexNum; j++)
{
printf("%4d",a[i][j]);
}
printf("\n");
}
}