找一个欧拉回路的路径问题

若一个无向图存在欧拉回路,则图中所有的点的度数都为奇数的点的个数为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");
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值