欧拉路径(回路)Hierholzer算法实现-C语言

  1. 上一篇博文中,我们提到欧拉路径有Hierholzer 和 Fleury 算法,Hierholzer相比而言逻辑清晰,而且比较有效。本文终点介绍Hierholzer的基本算法。Hierholzer算法利用深度优先搜索(DFS),最后完成所有的边的遍历。在图论中,常规的DFS算法的操作对象为顶点,这就导致可能已经完成所有顶点的遍历,但是有些边还没有遍历(比如backedge 或 forward edge类型)。我们以示意为例进行说明,
    在这里插入图片描述
    图中包含12条有向边,注意②->②成为自环有向边(不一定严谨),从顶点①出发,经过顶点③,再经过顶点⑤,而后经过顶点⑥,再遍历顶点②以及顶点④,最后回到顶点⑥,这时候显示全部遍历完成,无需再遍历任何顶点。
    实际情况是,5条白色的边仍然未遍历。如果采用上述方式,那么就无法完成欧拉路径(回路)的遍历过程。
  2. 如何解决上述问题
    要解决上述问题,我们就需要利对边进行标记,以边为对象进行深度优先搜索遍历,这样就可以确保所有的边都可以被遍历到。对于上述顶点,我们可以建立以下属性来跟踪每个顶点对应的边的访问情况,对于每个顶点我们定义in为入度数,定义out为出度数,同时我们跟踪邻接表的访问的顶点指针,用edge_ptr表示。 edge_ptr将被初始化为所有顶点的firstarc(可能为NULL),在每次访问后,向后移动,记录下一个边的指针(edge_ptr->nextarc). 我们可以通过判断edge_ptr是否为空,来确认DFS的访问是否需要结束。
/-------------------------严蔚敏《数据结构》邻接表定义--------------------------/
typedef struct ArcNode
{
    int              adjvex; //Vertex location this arc points to 本弧所指向的顶点
    struct ArcNode  *nextarc; //Pointer pointing to next Arc 指向下一条弧指针
    InfoType        *info;  //Related information pointer about THIS Arc,该弧相关指针
}ArcNode;

typedef struct VNode
{
    VertexType  data; //Vertex informaiton
    ArcNode    *firstarc; // First arc incident to this Arc
}VNode,AdjList[MAX_VERTEX_NUM];
/-------------------------严蔚敏《数据结构》邻接表定义------------------------/

typedef struct Euler_Node
{
    int in;
    int out;
    ArcNode *edge_ptr;
}Euler_Node, Euler_List[MAX_VERTEX_NUM];
  1. 利用Hierholzer算法进行DFS递归之前,还有几个问题需要亟待解决,首先需要判定有向图是否包含欧拉路径或欧拉回路,这就需要计算每个顶点的出度和入度大小,然后根据定义进行判断。计算入度、出度和此顶点边的指针,下面的初始化函数将完成此任务。
//@param list数组定义在结构体当中
void Init_list(ALGraph G, Euler_List list)
{
    int i;
    int v;
    int w;
    ArcNode *p;

    v=0;
	//初始化list数组
    for(i=0;i<G.vexnum;i++)
    {
        list[i].edge_ptr=G.vertices[i].firstarc;
        list[i].in=0;
        list[i].out=0;
    }

    for(v=0;v<G.vexnum;v++)
    {
        for(p=G.vertices[v].firstarc;p;p=p->nextarc)
        {
            w=p->adjvex;
            list[v].out++;
            list[w].in++;
        }
    }
}

利用各个顶点的出度和入度的相对大小比较,那么就能相对容易判断一个有向图(本文中的研究对象)是否含有欧拉路径(欧拉回路),根据欧拉路径或回路的定义,我们给出下列函数完成欧拉路径或欧拉回路的计算。

bool is_Euler_Path(ALGraph G, Euler_List list)
{
    int start_node;
    int end_node;
    int i;

    start_node=end_node=0;

    for(i=0;i<G.vexnum;i++)
    {
        //check whether Euler path is valid nor not
        //First check the diffrence between indegree and outdegree
        if((list[i].out-list[i].in>1) || (list[i].in-list[i].out>1))
        {
            return false;
        }
        else if (list[i].out - list[i].in==1)
        {
            start_node++;
        }
        else if (list[i].in - list[i].out == 1)
        {
            end_node++;
        }
    }

    return ((start_node==1 && end_node==1) || (start_node==0 && end_node==0));
}

当上述问题解决之后,我们还需要确认欧拉路径的起始点(欧拉回路可以选择任一点),在欧拉路径中,如果起始点选择不正确,那么就会导致无法完成正确的遍历。寻找欧拉路径(欧拉回路)起始点的函数描述如下。

int find_Start_Node(ALGraph G, Euler_List list)
{
    int start=0;
    int i;

    for(i=0;i<G.vexnum;i++)
    {
        if((list[i].out-list[i].in==1) && list[i].out>0)
        {
            start = i;
            break;
        }
    }

    return start;
}
  1. 上述问题解决之后,我们就可以利用DFS函数,并辅助线性表,对有向图进行欧拉路径或欧拉回路的搜索。 这是典型的DFS搜索,只是对象为边而不是顶点。
void DFS_Euler(ALGraph G, int s, Euler_List list, LinkList_E *path)
{
    Link_E link;
    ArcNode *p;
    int     w;

    while(list[s].edge_ptr)
    {
        p=list[s].edge_ptr;
        w=p->adjvex;
        list[s].edge_ptr=p->nextarc;
        list[s].out--;
        DFS_Euler(G,w,list,path);
    }
    
    MakeNode_E(&link,s); //参见严蔚敏线性表
    InsFirst_E(path,path->head,link);//参考严蔚敏先线性表,头部插入节点
}
  1. 把上述所有的函数组织起来,实现Hierholzer算法的最后目的。
Status find_Euler_Path(ALGraph G, Euler_List list, LinkList_E *path)
{
    int s;
    // initialize the list[i].in, list[i].out and list[i].edge_ptr
    Init_list(G,list); 
    InitList_E(path);

    if(!is_Euler_Path(G,list))
    {
        return ERROR;
    }

    s=find_Start_Node(G,list);
    DFS_Euler(G,s,list,path);

    return OK;
}

6.总结
我们可以形象的把Hierholzer算法理解为,不同的欧拉路径(欧拉回路)之间互相联结的过程。整个过程为大环套小环,最终环环紧相联(one splice)的过程。
在这里插入图片描述
以上,
谢谢

参考文献/资料:

  1. 《数据结构》严蔚敏
  2. Video, Finding Eulerian Paths and Circuits by William Fiset
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值