欧拉回路——Hierholzer算法

欧拉回路——Hierholzer算法

对于一个欧拉回路,我们分析其组成部分,一个欧拉回路必是由多个环连接在一起,才能符合所有的点入度与出度相等。

因此Hierholzer算法算法的核心就是依次寻找环,将所有的环拼合成一条欧拉回路。

过程

首先我的定义一个双端链表和一个队列,设链表为 T T T就是最后的欧拉回路的路径,队列 B B B类似于 B F S BFS BFS算法中的队列的作用。

队列中的元素是一个二元组 ( v , T v ) (v,T_{v}) (v,Tv),其中 v v v是图中节点的编号, T v T_{v} Tv是这个节点在链表 T T T的指针。

初始化队列中只有一个元素 ( 1 , N U L L ) (1,NULL) (1,NULL),一个任意节点(不一定是 1 1 1),在 T T T中的指针为 N U L L NULL NULL,因为此时 1 1 1未在 T T T中。

在Hierholzer算法中,我们依次取出队列的队首元素设为 ( v , p t r ) (v,ptr) (v,ptr),直到队列为空为止。

接下来,我将在图中搜索一个环,这个环以 v v v开始,以 v v v结束。计算出这个环之后,如果 p t r ptr ptr为空指针,那么就让 T T T等于这个环链表即可。如果 p t r ptr ptr不为空,则用这个环链表去替换 v v v T T T中的位置即可。如果这个环为空,即 v v v的出度为 0 0 0,那么就什么也不做。

代码

首先是链表定义:


struct Node
{
    int idx;
    Node *ptr;
    Node *next;
    Node *prev;
    Node() : next(nullptr), prev(nullptr), ptr(nullptr)
    {
    }
};

class LinkedList
{
private:
    Node *head;
    Node *tail;

public:
    LinkedList()
    {
        head = new Node;
        tail = new Node;

        head->next = tail;
        tail->prev = head;
    }

    Node *add(Node n)
    {
        Node *c = new Node(n);

        Node *prv = tail->prev;
        prv->next = c;
        c->prev = prv;

        c->next = tail;
        tail->prev = c;

        return c->prev;
    }

    bool empty()
    {
        return head->next == tail;
    }

    Node pop()
    {
        Node *tag = head->next;
        Node *nxt = head->next->next;

        head->next = nxt;
        nxt->prev = head;

        Node ans(*tag);
        delete tag;
        return ans;
    }

    void replace(LinkedList c, Node *n)
    {
        if(c.empty()) return;
        Node *prv = n->prev;
        Node *nxt = n->next;

        delete n;

        prv->next = c.head->next;
        c.head->next->prev = prv;

        nxt->prev = c.tail->prev;
        c.tail->prev->next = nxt;
    }

    void print()
    {
        Node * curr = head->next;
        while (curr != tail)
        {
            cout << curr->idx;
            curr = curr->next;
        }
        
    }
};

一个双端队列,添加了一个替换操作。

然后是环搜索算法:


LinkedList circle(int idx)
{
    LinkedList c;

    Node t;
    t.idx = idx;

    c.add(t);

    int u = idx;

    while (Deg[u] > 0)
    {
        int nxt = 0;
        for (int i = 1; i <= n; i++)
            if (Graph[u][i])
            {
                nxt = i;
                break;
            }

        Graph[u][nxt] = Graph[nxt][u] = 0;
        Deg[u]--;
        Deg[nxt]--;

        Node n;
        n.idx = nxt;
        Node *tag = c.add(n);

        if (Deg[u] > 0)
        {
            Node k;
            k.idx = u;
            k.ptr = tag;
            L.add(k);
        }
        u = nxt;
    }

    return c;
}

我们不断的沿着第一个边跑下去,并且边跑边把经过的边删除,直到出度为 0 0 0,并且,把路径中包含其他环的点继续加入到搜索队列中。

主函数搜索合并即可:

int main()
{
    FR;
    cin >> n >> m;

    while (m--)
    {
        int u, v;
        cin >> u >> v;
        Graph[u][v] = Graph[v][u] = 1;
        Deg[u]++;
        Deg[v]++;
    }

    LinkedList T;

    Node inital;
    inital.idx = 1;
    L.add(inital);

    while (!L.empty())
    {
        Node curr = L.pop();
        LinkedList c = circle(curr.idx);
        if (curr.ptr == nullptr)
            T = c;
        else
            T.replace(c, curr.ptr);
    }

    T.print();
    return 0;
}

证明

搜索环函数的正确性,我们假设搜索之前的图存在欧拉回路(即所有点的出度=入度),搜索之后要么为空,要么仍存在欧拉回路。

对于起始点 i d x idx idx来说,经过 i d x idx idx的之后, i d x idx idx的度将变成奇数度,除了 i d x idx idx点,其他点必是偶数度,因此每经过一次其他点,其他点的度都要减 2 2 2,或者说,能进入其他点,就必能从其他点出来,奇数点只有 i d x idx idx,因此,while循环必将停在 i d x idx idx上。当搜索结束之后,点 i d x idx idx的度必为 0 0 0,并且 i d x idx idx所在的所有环都将并入到 T T T中。此时图要么仍存在欧拉回路,要么为空。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值