欧拉回路其实就是我们俗称的“一笔画”问题
好,先提出一点问题,欧拉回路是什么?
在回答这个问题前,首先要说的是欧拉路径
如果在一个图中,可以找到一条路径,使得路径对图中的每一条边正好经过一次,则这条路径被称为欧拉路径;
当欧拉路径的起点与终点相同时,又被称为欧拉回路。
判断欧拉回路是否存在
如果有奇数桥的地方不只两个,则欧拉回路不存在
如果均为偶数桥,则任意一点出发均能找到欧拉回路
如果只有两个地方有奇数桥,可以从这二者之一出发,寻找到欧拉回路。
寻找欧拉回路
基本思想:深度遍历
先对一个顶点进行深度遍历,得到一段路径
在这段路径上寻找还有边未被访问到的顶点,进行深度遍历,将得到的新的路径拼接到原有路径中
直到所有的边都被访问
直接上代码,
代码输入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);
/
}