欧拉路径的Fleury算法-C 语言实现

本文详细介绍了Fleury算法在寻找无向连通图的欧拉路径中的应用,该算法基于深度优先遍历,重点在于判断和处理桥边。通过从具有奇数度的顶点出发,选择非桥边进行遍历,逐步构建欧拉路径。文章提供了C语言实现的函数接口,并强调了算法中回溯和恢复边的重要性。
摘要由CSDN通过智能技术生成

欧拉路径的Fleury算法-C语言实现

我们前面介绍了Hierholzer算法求解有向连通图的欧拉路径(欧拉回路)的算法,这个算法可以简单理解为环环相连,最终求得欧拉路径,整个过程利用深度优先遍历思想,以图中的边为遍历对象,完成遍历后即可求得相应的欧拉路径(欧拉回路),这个算法逻辑清晰,程序运行效率较高。

本文将介绍另外一种欧拉路径的算法,终点介绍无向连通图中的欧拉路径,主要算法为Fleury算法,此算法的核心思想为,如果现有顶点点的度为1,那么和此度相关的唯一路径标记为有效路径,Fleury算法可以立即进行处理;否则就需要利用类似回溯的思想,来评估这些路径是否为bridge. 那么问题就来了,如何定义某顶点到另外一个顶点的路径为bridge呢?

思路很简单,我们先从某顶点(v)出发,评估此顶点直接或间接可达(reachable)的顶点数目,记为count1; 然后我们评估(v,w)是否为桥,这时候,我们在图中先移除此边,如果顶点(v)可达的定点数量count2比count1要小,那么(v,w)就成为一桥边(bridge edge)。无论此边是否为桥边,我们再操作后,都需要把这条边重新加入到图中去。是否不是听起来有点熟悉,这就是回溯的经典思想,后面就这个问题我们在程序中将进一步展开。

一图胜千言,我们借助一张图,来对上面的桥边进行进一步的理解。

在这里插入图片描述

读者先暂停几秒,思考一下,如果此图中存在欧拉路径(欧拉回路),起点是哪个? 显而易见,我们应该从C或D出发,才能完成欧拉路径的搜索,起点位置的选择是欧拉路径的性质决定的,只能从奇数点出发(如果是欧拉路径而不存在欧拉回路)。在上图中,如果从C定点出发,我们可以有3个选择,可以选择(C,D)、(C,A)、(C,B),针对这三个选择,我们如何判断哪条边是正确的选择呢(非桥边)。我们利用回溯方法逐一判断知道找到一条非桥边。

如果第一步,我们尝试选择(C,D), 我们从C点出发,在保持(C,D)边的前提下,利用DFS对顶点进行深度优先遍历,得到count 1=4(包括C顶点本身);然后我们尝试从无向图中移除(C,D)边,移除后的D顶点变为孤岛。如果再次从C点开始进行DFS深度优先搜索,我们容易得到count2=3。由于移除(C,D)后,从C点能到达的顶点数量减少,那么我们就认为(C,D)为桥边。

在这里插入图片描述

值得注意的是,我们再尝试两次DFS遍历顶点后,需要回复之前移除的边,以便后续能继续访问。恢复边后的图如下:

在这里插入图片描述

我们可以尝试评估(C,A)是否为桥边,同理,我们先从C点出发对图上的顶点进行DFS遍历,遍历完成后得到count1=4; 然后,我们尝试去掉(C,A)边,再次进行DFS对顶点的遍历,我们得到count2=4; 由于两次遍历的定点数没有减少,所以我们可以判断(C,A)为非桥边。也就是,我们即使再Fleury算法中真正删除(C,A)后,仍然可以通过其他路径回到C点。

Fleury思想的特点之一,如果每个顶点关联多条路径,在跨出每一步之前,都要确保即使断了这条路,也有可能回到原来的顶点,真正做到即使“过河拆路”,也可能回到原点。

我们回到主题,现在我们解决了桥边的判断问题,如果顶点关联的边多余1条,我们总是从非桥边开始往前遍历。关于桥边的阐述我们可以暂时告一段路,我们接下来就要回到Fleury算法本身。

Fleury算法的核心思想为,找到一条非桥边或独边,以边为遍历对象,逐边进行DFS遍,遍历后,把此边删除(真正删除)。不断利用DFS对边进行遍历,直至遍历结束。

接下来,我们谈一下算法的实现,我们在算法中采用C语言和严蔚敏《数据结构》中的邻接矩阵:

a) 函数的头文件说明(Euler_Path.h):

/**
 * @file Euler_Path.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-02-11
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef EULER_PATH_H
#define EULER_PATH_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "../00_introduction/Status.h"
#include "../01_Matrix_Graph/MatrixGraph.c"


 

/**
 * @brief Find the Euler path(circuit) in the graph
 *
 * @param G Matrix Graph
 * @param path Store the Euler index
 * @param degree Degree of each node in the undirected graph
 * 
 * @return Status -return error if there is Eurler path or circuit
 */
Status find_Euler_Path(MGraph *G, int *path,int *degree);


/**
 * @brief Count the degree of each node
 * 
 * @param G MGraph G
 * @param degree Degree array list
 */
void count_Degree(MGraph G, int *degree);

/**
 * @brief Check if the graph contains Euler path or not
 * @param G MGraph G
 * @param degree Degree array for each node

 * @return true if graph contains Euler path
 * @return false if graph doesn't contain Euler path
 */
bool is_Euler_Path(MGraph G,int *degree);

/**
 * @brief Find the start node in the graph according to the Euler path characteristic
 *
 * @param G Matrix graph
 * @param list Euler_list
 * @return int -Node index
 */
int find_Start_Node(MGraph G, int *degree);

/**
 * @brief Use depth first search to work out the Euler path/circuit
 *
 * @param s Start point
 * @param list Node property array
 * @param path Store the Euler path array
 */
void DFS_Euler(MGraph *G, int s, int *path,int *degree);

/**
 * @brief  This function returns count of vertices reachable from v. 
 * It does DFS
 *
 * @param G Matrix graph
 * @param u Start point
 * @return int Return the count of vertices reachable from v
 */
int DFS_Count(MGraph G, int u);

/**
 * @brief Add the (v,w) edge to Graph
 * 
 * @param G Pointer to Matrix Graph
 * @param v Vertex point 1
 * @param w Vertex point 2
 */
void Add_Edge(MGraph *G, int v, int w,int *degree);

/**
 * @brief Remove the (v,w) edge to Graph
 *
 * @param G Pointer to Matrix Graph
 * @param v Vertex point 1
 * @param w Vertex point 2
 */
void Remove_Edge(MGraph *G,int v, int w,int *degree);

/**
 * @brief Check if the (v,w) is a valid arc(not a bridge)
 * 
 * @param G Pointer to Matrix graph
 * @param v Vertex v
 * @param w Vertex w
 * @return true If not a bridge, return true
 * @return false If a bridge ,return false
 */
bool isValidEdge(MGraph *G,int v,int w,int *degree);

/**
 * @brief Show Euler path
 * 
 * @param G Matrix Graph
 * @param path Path array
 */
void show_EulerPath(MGraph G, int *path);

/**
 * @brief Initialize the degree and path
 *
 * @param G Matrix Graph
 * @param degree Degree of each node in the undirected graph
 * @param path Store the path index of Euler path
 */
void Init_Param(MGraph G, int **degree,int **path);

#endif

b) 函数的实现(Euler_Path.c)

/**
 * @file Euler_Path.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-02-11
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef EULER_PATH_C
#define EULER_PATH_C
#include "Euler_Path.h"

Status find_Euler_Path(MGraph *G, int *path, int *degree)
{
    int s;

    count_Degree(*G,degree);

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

    s = find_Start_Node(*G,degree);
    DFS_Euler(G,s,path,degree);

    return OK;
}

void count_Degree(MGraph G, int *degree)
{
    int i; //row indicator
    int j; //column indicator

    for(i=0;i<G.vexnum;i++)
    {
        
        for(j=0;j<G.vexnum;j++)
        {
            if(G.arcs[i][j].adj)
            {
                degree[i]++;
            }
        }
    }

    return;
}

bool is_Euler_Path(MGraph G, int *degree)
{
    int i;
    int odd_degree;

    odd_degree=0;

    for(i=0;i<G.vexnum;i++)
    {
        if(degree[i]%2!=0)
        {
            odd_degree++;
        }
    }

    return (odd_degree==0) || (odd_degree==2);
}

int find_Start_Node(MGraph G, int *degree)
{
    int start;
    int i;

    start=0;

    for(i=0;i<G.vexnum;i++)
    {
        if (degree[i] % 2 != 0)
        {
            start=i;
            break;
        }
    }

    return start;
}


void DFS_Euler(MGraph *G, int s, int *path, int *degree)
{
    int v;
    int w;
    static int index=0;
    v = s;
    
    *(path+index)=v;
    index=index+1;

    for (w = FirstAdjVex(*G, G->vexs[v]); w >= 0; w = NextAdjVex(*G, G->vexs[v], G->vexs[w]))
    {
        if (isValidEdge(G, v, w, degree))
        {
            Remove_Edge(G, v, w, degree);
            DFS_Euler(G, w, path, degree);
        }
    }

}

int DFS_Count(MGraph G, int u)
{
    int count;
    int v;
    int w;

    visited[u]=1;
    count=1;
    v=u;

    for (w = FirstAdjVex(G, G.vexs[v]); w >= 0; w = NextAdjVex(G, G.vexs[v], G.vexs[w]))
    {
        if(!visited[w])
        {
            count=count+DFS_Count(G,w);
        }
    }

    return count;
}

void Add_Edge(MGraph *G, int v, int w,int *degree)
{
    G->arcs[v][w].adj=1;
    G->arcs[w][v].adj=1;
    degree[v]++;
    degree[w]++;
    return;
}

void Remove_Edge(MGraph *G, int v, int w,int *degree)
{
    G->arcs[v][w].adj=0;
    G->arcs[w][v].adj=0;
    degree[v]--;
    degree[w]--;
    return;
}

bool isValidEdge(MGraph *G, int v, int w, int *degree)
{
    int count1; //before (v,w) is removed in the graph
    int count2; //after (v,w) is removed in the graph

    count1=0;
    count2=0;

    if(degree[v]==1)
    {
        return true;
    }

    memset(visited,0,sizeof(int)*MAX_VERTEX_NUM);
    count1=DFS_Count(*G,v);

    Remove_Edge(G,v,w,degree);

    memset(visited, 0, sizeof(int) * MAX_VERTEX_NUM);
    count2=DFS_Count(*G,v);

    Add_Edge(G,v,w,degree);

    return count1<=count2;
}

void show_EulerPath(MGraph G, int *path)
{
    int i;
    for(i=0;i<G.arcnum+1;i++)
    {
        printf("-%c-",G.vexs[path[i]]);
    }

    printf("\n");
}

void Init_Param(MGraph G, int **degree, int **path)
{
    *degree=(int *)malloc(sizeof(int)*G.vexnum);
    *path = (int *)malloc(sizeof(int) * (G.arcnum+1));

    memset(*degree,0,sizeof(int)*G.vexnum);
    memset(*path, 0, sizeof(int) * (G.arcnum + 1));

    return;
}

#endif

c) 主函数和测试(Euler_Path_main.c)

/**
 * @file Euler_Path_main.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-02-12
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef EULER_PATH_MAIN_C
#define EULER_PATH_MAIN_C
#include "Euler_Path.c"

int main(void)
{
    int *path;
    int *degree;
    MGraph G;
    FILE *fp;


    fp=fopen("UDG.txt","r");
    CreateGraph(&G,fp);

    Init_Param(G,&degree,&path);

    find_Euler_Path(&G,path,degree);

    show_EulerPath(G,path);
    
    PressEnter;
    return EXIT_SUCCESS;
}

#endif

总结,本文的算法略显复杂,但是对DFS的深入理解很有帮助,建议读者能根据Fleury算法原理,尝试自己动手,实现此算法。

以上,
谢谢

参考

  1. 严蔚敏《数据结构》
  2. Eulerian path and circuit for undirected graph
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值