图遍历的应用之寻找欧拉回路

欧拉回路其实就是我们俗称的“一笔画”问题

好,先提出一点问题,欧拉回路是什么?

在回答这个问题前,首先要说的是欧拉路径

如果在一个图中,可以找到一条路径,使得路径对图中的每一条边正好经过一次,则这条路径被称为欧拉路径;

当欧拉路径的起点与终点相同时,又被称为欧拉回路。

判断欧拉回路是否存在

如果有奇数桥的地方不只两个,则欧拉回路不存在

如果均为偶数桥,则任意一点出发均能找到欧拉回路

如果只有两个地方有奇数桥,可以从这二者之一出发,寻找到欧拉回路。

寻找欧拉回路 

基本思想:深度遍历

先对一个顶点进行深度遍历,得到一段路径

在这段路径上寻找还有边未被访问到的顶点,进行深度遍历,将得到的新的路径拼接到原有路径中

直到所有的边都被访问

直接上代码,

代码输入n,m分别代表顶点数,边数

输入m行,每行的u,v表示顶点u与v之间有边

如果存在欧拉回路,输出从节点0出发的欧拉回路路径

如果不存在欧拉回路,输出“不存在欧拉回路”

#include<bits/stdc++.h>
using namespace std;
const int mxn = 10005;
//图的存储:采用邻接表存储方式
class adjMatrixGraph
{
public:
    adjMatrixGraph(int vsize);
    void insert(int u,int v);
    void remove(int u, int v);
    void EulerCiruit(int start);
    ~adjMatrixGraph();//析构函数
private:
    int vers,edges;//vers代表顶点数,edges代表边数
    struct edgeNode//创建边表结构体,end代表顶点下标,weight代表指向下一条边的权重,next指针指向下一个连通的顶点
    {
        int end;//起始点
        edgeNode *next;//指向点
        edgeNode(int e,edgeNode *n=NULL)//写一个构造函数方便后续使用
        {
            end=e;
            next=n;
        }
    };
    struct verNode//邻接表头即节点表的结构体,ver代表顶点名称A,B……,head指针指向当前顶点指向的下一个节点(是由边表节点edgeNode表示的)
    {
        edgeNode *head;
        verNode(edgeNode *h=NULL) {head=h;}
    };
    struct EulerNode//创建欧拉路径中的结点类
    {
        int NodeNum;//结点的下标
        EulerNode *next;
        EulerNode(int ver){NodeNum=ver;next=NULL;}
    };
    verNode *verList;//创建数组形式的节点表
    verNode *clone()//创建邻接表的拷贝,以便在找完路径后能恢复这个图的邻接表,因为在访问路径后为防止重复访问,将已访问的路径删除
    {
        verNode *tmp=new verNode[vers];//开了一片新的邻接表动态数组
        edgeNode *p;//定义了一个边结点
        for(int i=0;i<vers;i++)//将每一个顶点结点的边节点复制给新建的tmp的节点
        {
            p=verList[i].head;
            while(p!=NULL)
            {
                tmp[i].head=new edgeNode(p->end,tmp[i].head);//采用的依旧是插脖子的方法,但是这时顺序就反过来了
                p=p->next;
            }
        }
        return tmp;
    }
    EulerNode *EulerCircuit(int start,EulerNode *&end)//私有的寻找欧拉回路函数,从start开始深度搜索,返回一段待拼接的路径,同时更新链表的end尾结点
    {
        EulerNode *beg;
        int nextNode;
        beg=end=new EulerNode(start);//因为要不断更新end所以将end赋值给了beg
        while(verList[start].head!=NULL)//顶点结点的边表存在时,进行循环操作,就是一直深度遍历直到无通路
        {
            nextNode=verList[start].head->end;//将当前顶点的邻接顶点下标赋值给nextNode
            remove(start,nextNode);
            remove(nextNode,start);//双向删除
            start=nextNode;//更新start的值
            end->next=new EulerNode(start);//end的作用就是创建新的节点
            end=end->next;
        }
        return beg;//beg为这段路径的头结点,end为尾结点
    }
};
//构造函数
adjMatrixGraph::adjMatrixGraph(int vsize)
{
    vers=vsize;
    edges=0;
    verList=new verNode[vsize];
}
//析构函数
adjMatrixGraph::~adjMatrixGraph()
{
    edgeNode *p;
    for(int i=0;i<vers;i++)
    {
        while((p=verList[i].head)!=NULL)
        {
            verList[i].head=p->next;
            delete p;
        }
    }
    delete [] verList;
}
//插入函数
void adjMatrixGraph::insert(int u, int v)
{
    verList[u].head=new edgeNode(v,verList[u].head);
    ++edges;
}
void adjMatrixGraph::remove(int u, int v)
{
    edgeNode *p=verList[u].head,*q;

    if(p==NULL) return;
    if(p->end==v)
    {
        verList[u].head=p->next;
        delete p;
        --edges;
        return;
    }
    while(p->next!=NULL&&p->next->end!=v) p=p->next;
    if(p->next!=NULL)
    {
        q=p->next;
        p->next=q->next;
        delete q;
        --edges;
    }
}
void adjMatrixGraph::EulerCiruit(int start)
{
    EulerNode *beg,*end,*p,*q,*tb,*te;
    int numOfDegree,i;
    edgeNode *r;
    verNode *tmp;
    //检查每个顶点,判断是否存在欧拉回路
    for(int i=0;i<vers;i++)
    {
        numOfDegree=0;//每个顶点的度数
        r=verList[i].head;
        while(r!=0)
        {
            ++numOfDegree;//只要还有邻接点就加1
            r=r->next;
        }
        if(numOfDegree==0||numOfDegree%2)//对于每个节点,必须有偶数个桥才可能有欧拉回路
        {cout<<"不存在欧拉回路"<<endl;return;}
    }
    tmp=clone();//将拷贝好的邻接表存入tmp中
    beg= EulerCircuit(start,end);//先计算一段路径
    while(true)
    {
        p=beg;
        while(p->next!=NULL)//下一个节点非空
            if(verList[p->next->NodeNum].head!=NULL) break;//如果这个节点边表非空的话,跳出循环
            else p=p->next;//如果没有边表的话就把p移向下一位
            //找到了需要进行深度遍历的顶点
            if(p->next==NULL) break;//全部边都访问过了
            q=p->next;
            tb= EulerCircuit(q->NodeNum,te);//得到一段新路径,te为尾结点
            te->next=q->next;//这里跳过了p->next
            p->next=tb;//将tb到te插入p与p->next之间
            delete q;
    }
    delete []verList;
    verList=tmp;//恢复原图
    cout<<"欧拉回路是:"<<endl;
    while(beg!=NULL)
    {
        cout<<beg->NodeNum<<'\t';
        p=beg;//一边输出一遍删除
        beg=beg->next;
        delete p;
    }
    cout<<endl;
}
int main(void){
    int n,m;
    cin>>n>>m;
    adjMatrixGraph graph(n);
    for(int i=0;i<m;i++){
        int u,v;
        cin>>u>>v;
        // your code
        graph.insert(u,v);
        graph.insert(v,u);
    }
    graph.EulerCiruit(0);
    /
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值