找一個歐拉回路的路徑問題

若一個無向圖存在歐拉回路,則圖中所有的點的度數都爲奇數的點的個數爲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");
	}
}


發佈了21 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章