在广度优先算法中,有几个问题值得探讨。
1、使用递归
这个方法是欧拉回路问题中最广泛使用的一种方法。如果一个无向图是联通的,且最多只有两个奇点(就是度数为奇数的点),就一定存在欧拉回路。其中,两个奇点一定是一个为起点,另一个是终点,这样构成“从一个奇点出发,到另一个奇点回来”。起点的入度比出度大一,终点出度比入度大一。
于此同时,我们还需要证明:当一个图G存在欧拉回路,除去一个结点外,剩下的路径仍然构成欧拉回路。当我们在欧拉回路中减掉一个点的时候,肯定这个点连接的两条边也会被删除。假设结点A相邻的两个结点是B和C,如下图所示:
可以看到,当A点删除之后,可以知道B的出度减少1,C的入度也减少一。所以(indegree-1)+(indegree+1)=indegree,可以发现图的入度和出度仍然不变,由此可见,欧拉回路中删除一个点,剩下的图仍然构成欧拉回路。
依据数学归纳法,我们可以在欧拉回路中对点进行递归。
void euler(int u)
{
for(int v=0;v<n;v++){
if(G[u][v]&&!visit[u][v])
{
visit[u][v]=visit[v][u]=1;
enler(v)
cout<<u<<v<<endl;
}
}
}
这样完成了递归,递归的方法是比较简洁的,容易懂。下面我提供一种用辅助队列的方法。
2、辅助队列的方法,这种方法和递归比,优势在于占用的空间更少。(这种方法为本人原创,转载请注明出处,同时也欢迎提出不对的地方)
基本的思路示意如上。
1、我们从起点开始出发往终点走,对走过的路径标记为蓝色。
为此,我们需要给边加上color属性
struct edge
{
int start;
int end;
edge *next;
int color;
edge(int s,int e):start(s),end(e),color(0),next(NULL){}
}
2、对于有分支边的点,比如说E,G两个点,我们把未走过的边入队列,(或者入栈),队列和栈都是用来辅助的。
3、当我们找到一条欧拉回路的时候,就是cycle_e-->end==i,表示找到回路了。这个时候我们去栈中寻找,用来回溯。回溯到栈/队列中的那个id,继续走栈中保存的分支边。
直到栈为空,退出循环。
我把结果保存在了链表中。
Linklist *FindEulerWalk(link_graph *gl,linklist *result)
{
linklist walk;
sqQueue crossnode; //用来存放分支点
for(int i=0;i<numvertex;i++)
{
cleancolor(gl->v[i]);
initqueue(crossnode); //每次循环要更新颜色和队列
getvertexdegree(gl,gl->v[i]) //获取图中每个结点的入度和出度,这个函数挺好写的
if(gl->v[i]->outdegree==gl->v[i]->indegree && gl->v[i]->outdegree!=0)
{
edge *cycle_e=gl->v[i]->firstedge; //cycle_e是在循环中遍历行走的边
if(cycle_e->next && cycle_e->color==WHITE) //如果遇到了分支点,并且分支边没有走过,就把它入栈或入队列
enqueue(&crossnode,cycle_e->next)
while(cycle_e)
{
if(cycle_e->end!=i)
{
listinsert(&walk,cycle_e) //存放满足条件的边
cycle_e->color=BLUE;
int cur_id=gl->v[cycle_e->end]->id;
cycle_e=gl->v[cur_id]->firstedge;
if(cycle_e->next && cycle_e->color==WHITE)
enqueue(&crossnode,cycle_e->next) //每一步循环,要检查一下分支边是否存在,如果存在就入队列
}
else
{
if(isemptyqueue(crossnode))
break;
else
{
listinsert(&walk,cycle_e); //将最后一条边入队列
cycle_e->color=BLUE;
combinelist(&result,&walk); //这一步执行的功能是将路径队列合并到结果链表中,把路径中符合条件的边放到结果链表result中
clearlist(&walk);
cur_id=dequeue(&crossnode);
cycle_e=gl->v[cur_id]->firstedge;
while(cycle_e->next && cycle_e->color!=WHITE)
cycle_e=cycle_e->next;
}
}
}
}
}
return result;
}
这里用的是广度优先搜索
3、还可以用深度优先搜索,前向边和后向边来判断。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。