您的位置:首页 > 其它

图论欧拉路径问题(单词接龙)

2016-12-22 14:21 225 查看
查看原文:点击打开链接

<h2>定义</h2>

欧拉问题分为欧拉路径以及欧拉回路。

<ul>

  <li>欧拉路径,指在图中找得到一条路径,使得该路径对图的每一条边恰好访问一次。</li>

  <li>欧拉回路,指在图中找得到一个圈,使得该圈恰好经过每一条边一次。</li>

</ul>

由上可见,路径与回路的区别仅在于起点与终点是否是同一个点。

<h2>无向图判定定理</h2>

首先保证图G是一个连通的图。

<ul>

  <li>无向图G存在欧拉回路的充要条件是,图G里所有顶点的度为偶数。</li>

  <li>无向图G存在欧拉路径的充要条件是,图G里有且仅有两个奇度数的顶点,其分别为路径的起点及终点。</li>

  <li>若图G里有多余两个的奇度数顶点,则不存在欧拉路径。</li>

</ul>

<h2>有向图判定定理</h2>

判定有向图的欧拉问题时,首先得判定有向图的基图(即其无向图)是连通的。

<ul>

  <li>有向图G存在欧拉回路的充要条件是,图G里每一个顶点的入度与出度都相等。</li>

  <li>有向图G存在欧拉路径的充要条件是,存在两个顶点作为起点与终点,其中起点的入度比出度少1,终点的入度比出度多1。</li>

</ul>

<h2>算法</h2>

第一个算法就是上边讲的,去统计每个顶点的度数,然后判断基图是否是连通的。

第二个算法由《数据结构与算法分析》给出,其复杂度仅为O(V+E),称其为套圈法,待以后分析。

<h2>例子:单词接龙</h2>

<blockquote>拉姆刚开始学习英文单词,对单词排序很感兴趣。如果给拉姆一组单词,他能够迅速确定是否可以将这些单词排列在一个列表中,使得该列表中任何单词的首字母与前一单词的尾字母相同。你能编写一个程序来帮助拉姆进行判断吗?

输入描述:

输入包含多组测试数据。对于每组测试数据,第一行为一个正整数n,代表有n个单词。然后有n个字符串,代表n个单词。保证:2<=n<=200,每个单词长度大于1且小于等于10,且所有单词都是由小写字母组成。

输出描述:

对于每组数据,输出"Yes"或"No"

输入例子1:

3

abc

cdefg

ghijkl

输出:

Yes

输入例子2:

4

abc

cdef

fghijk

xyz

输出:

No

输入例子3:

3

aba

cdc

efe

输出:

No

输入例子4:

4

abc

cde

cfg

ghc

输出:

YES</blockquote>

 

这道题在以前我使用递归的方式做过,不过复杂度比较高,去检测了把每一个单词当成首单词后的每一种情况。具体见博文:

>http://www.wyblog.cn/2016/05/21/%E9%A2%98%E7%9B%AE2-%E5%8D%95%E8%AF%8D%E6%8E%A5%E9%BE%99/

实际上这道题就是一个有向图的欧拉路径问题。我们把每个单词看做一条边,单词首字母为起点,单词尾字母为终点,那么这就是一个求有向图的欧拉路径问题。需要做的工作就是两部分,一是判断图是否是连通的,而是判断是否含有欧拉路径或者欧拉回路。代码如下:

```

#include<cstdio>

#include<iostream>

#include<queue>

#include<algorithm>

using namespace std;

#define MAX_VERTEX_NUM 27

int UnDirecCounter; //统计无向图的顶点个数,用于判定无向图是否是连通的

int DirecCounter; //统计有向图顶点个数 

int Visit[MAX_VERTEX_NUM]; //标记顶点是否被访问过 

 

void Init_Globalpara(void)

{
UnDirecCounter=0;
memset(Visit,0,sizeof(Visit));

}

typedef struct EdgeNode

{
int adjVertex;
int isDirecEdge; //标记此边是否为有向边 
EdgeNode *nextEdgeNode;

}EdgeNode;

typedef struct VerNode

{
int data;
int inDegree;
int outDegree;
EdgeNode *firstedge;

}VerNode;

typedef struct Graph

{
VerNode verNode[MAX_VERTEX_NUM];
int vertex_num,edge_num;

}Graph;

void CreateDAG(Graph &G,int n) 

{
int k;
char word[10];
char i,j;
G.edge_num=n;
EdgeNode *p;

for(k=1;k<=26;k++) //初始化头结点
{
G.verNode[k].data=k;
G.verNode[k].inDegree=0;
G.verNode[k].outDegree=0;
G.verNode[k].firstedge=NULL;

}

for(k=1;k<=n;k++) //同时创建有向图及无向图 
{
cin>>word;
i=word[0]; //首字母 
j=word[strlen(word)-1]; //尾字母

if(!Visit[i-'a'+1]) //计算无向图顶点个数 
{
Visit[i-'a'+1]=1;
UnDirecCounter++; 
}
if(!Visit[j-'a'+1]) 
{
Visit[j-'a'+1]=1;
UnDirecCounter++; 
}

G.verNode[j-'a'+1].inDegree++; //更新入度及出度 
G.verNode[i-'a'+1].outDegree++;

p=new EdgeNode;
p->isDirecEdge=1; //有向边,用来标志有向图 
p->adjVertex=(j-'a'+1);
p->nextEdgeNode=G.verNode[i-'a'+1].firstedge;
G.verNode[i-'a'+1].firstedge=p; 

p=new EdgeNode;
p->isDirecEdge=0; //无向边 
p->adjVertex=(i-'a'+1);
p->nextEdgeNode=G.verNode[j-'a'+1].firstedge;
G.verNode[j-'a'+1].firstedge=p;
}

}

int isWordList(Graph &G)

{
//首先判断有向图的基图是否为弱连通的 
int isConnected=0;
VerNode V;
EdgeNode *w;
queue<VerNode> Q;
int start=1;
int tempCount=0;
while(!Visit[start]) start++; //寻找图的一个起点 
memset(Visit,0,sizeof(Visit)); //清空Visit,以便DFS时记录
Q.push(G.verNode[start]);
Visit[start]=1;
while(!Q.empty())
{
V=Q.front();Q.pop();
tempCount++;
w=V.firstedge;
while(w)
{
if(!Visit[w->adjVertex])
{
Q.push(G.verNode[w->adjVertex]);
Visit[G.verNode[w->adjVertex].data]=1;
}
w=w->nextEdgeNode;
}
}
if(tempCount==UnDirecCounter) //遍历到的顶点数等于图的顶点数,则为连通的 
isConnected=1;

//然后寻找欧拉路径或者欧拉回路 
int hasEuler=1;
int startNum=0; //统计起点及终点个数 
int endNum=0;
for(int v=1;v<=26;v++)
{
if((G.verNode[v].outDegree-G.verNode[v].inDegree)==1)
startNum++;
if((G.verNode[v].inDegree-G.verNode[v].outDegree)==1)
endNum++;
if(abs(G.verNode[v].inDegree-G.verNode[v].outDegree)>1)
{
hasEuler=0;
break;

}
}
int isEulerPath=(startNum==1 && endNum==1); //包含欧拉路径
int isEulerCircuit=(startNum==0 && endNum==0); //包含欧拉回路 
if((!isEulerPath)&&(!isEulerCircuit))
hasEuler=0;

 
if(hasEuler&&isConnected)
return 1;
else
return 0; 

}

int main()

{
int n;
cin>>n;
Graph G;
Init_Globalpara();
CreateDAG(G,n);
if(isWordList(G))
cout<<"Yes"<<endl;
else
cout<<"No"<<endl; 

}

/*****

输入例子1:

3

abc

cdefg

ghijkl

输出:

Yes

输入例子2:

4

abc

cdef

fghijk

xyz

输出:

No

输入例子3:

3

aba

cdc

efe

输出:

No

输入例子4: 

4

abc

cde

cfg

ghc

输出:

YES 

*****/

```
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: