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