图算法——欧拉回路问题的解答

在广度优先算法中,有几个问题值得探讨。
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、还可以用深度优先搜索,前向边和后向边来判断。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值