你了解欧拉回路吗?(附Java实现代码)

一:什么是欧拉回路?

不知道你有没有玩过这样一种叫“一笔画”,从某一点开始画一个图形或图案,期间笔不能从纸上离开而且每条边只能画一次。

下面有三个例子,你可以先试一试看看能不能“一笔画”

第一个图其实是根本画不出来的,第二个图可以画出,但是不存在起点和终点为同一点的情况,第三个图可以轻松画出,而且存在起点和终点为同一点的情况

欧拉回路问题:
如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。
如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit)。
具有欧拉回路的图称为欧拉图(简称E图)。具有欧拉路径但不具有欧拉回路的图称为半欧拉图。

二: 无向图中欧拉回路存在的条件

什么情况下才存在欧拉回路呢?
充要条件:当且仅当图是连通的而且每个顶点的度是偶数
想想一下,如果一个节点v的度为1,那么进入v后只能留在v中,不可能再出来。

但是当有两个度为奇数的节点分别作为起点和终点,欧拉路径还是有可能存在的,如果奇数度的顶点多余两个,连欧拉路径都不可能存在。

三:如何得到欧拉回路

求解起点和终点重合的欧拉回路问题可以基于深度优先思想来实现,与DFS算法的区别就是,DFS算法中每个节点基本上只会访问一遍,而欧拉回路算法中要求放宽,只是每个边只能访问一边,但是某一个节点可以多次访问,但基本思想是一致的。DFS算法

深度优先搜索的主要问题在于当访问返回开始节点时,可能还剩下某些边没有访问到,也就是访问提前结束了,比较好补救方法就是,在那些没有访问到的路径的第一个节点重新开始新一轮深度优先搜索,将新的回路加到原来回路中,继续此过程直到所有的边都被遍历为止

四:Java实现

import java.util.*;


/**
 * @author ht113
 */
public class EulerCircuit {

    private List<Integer> path;

    /**
     * this cost O(|E| + |V|) time
     * @param unDirectedEdges: adjacency matrix,[1,2] represents edge from node1 to node2
     * @param n: the num of nodes
     * @param k: the start node of Euler Circuit
     * @return
     * @throws NotFoundException
     */
    public List<Integer> EulerCircuitByDFS(int[][] unDirectedEdges, int n, int k) throws NotFoundException{
        if (unDirectedEdges==null||unDirectedEdges.length<=1||n<=2) {
            throw new NotFoundException();
        }
        //init undirected graph,use adjacency list
        //{key:1, value:<2, 3>} represents edge from node1 to node2,node3
        Map<Integer, List<Integer>> graph = new HashMap<>();
        //making graph takes O(E)
        //iterate the adjacency matrix
        for(int i = 0; i<unDirectedEdges.length; i++) {
            int[] edge = unDirectedEdges[i];
            //add (edge[0], edge[1])
            if (!graph.containsKey(edge[0])) {
                graph.put(edge[0], new ArrayList<Integer>());
            }
            graph.get(edge[0]).add(edge[1]);
            //add (edge[1], edge[0])
            if (!graph.containsKey(edge[1])) {
                graph.put(edge[1], new ArrayList<Integer>());
            }
            graph.get(edge[1]).add(edge[0]);
        }
        path = new ArrayList<>();
        //ECDFS takes O(V)
        try {
            ECDFS(graph, k, k, path);
        }catch (NotFoundException e){
            throw e;
        }
        return path;
    }

    /**
     * special dfs for Euler Circuit
     * @param graph
     * @param k: start node
     * @param origin: the origin start node
     * @param currentPath
     * @throws NotFoundException
     */
    public void ECDFS(Map<Integer, List<Integer>> graph, int k, int origin, List<Integer> currentPath)
            throws NotFoundException{
        currentPath.add(k);
        for(int i = 0; i<graph.get(k).size(); i++){
            int neighbor = graph.get(k).get(i);
            //and the degree of node w is odd
            if(neighbor!=origin && graph.get(neighbor).size()%2!=0){
                throw new NotFoundException();
            }
            graph.get(k).remove(i);
            graph.get(neighbor).remove(Integer.valueOf(k));

            //when dfs return to the origin start node
            //some edges may not have been visited
            if(neighbor==origin){
                currentPath.add(origin);
                boolean allSeen;
                do{
                    boolean tmp = true;
                    for(int j = 0; j<currentPath.size(); j++) {
                        int entryNode = currentPath.get(j);
                        tmp &= graph.get(entryNode).size() == 0;
                        if(!tmp) {
                            List<Integer> tempPath = new ArrayList<>();
                            ECDFS(graph, entryNode, entryNode , tempPath);
                            //add child circuit path
                            int index = currentPath.indexOf(entryNode);
                            currentPath.remove(index);
                            currentPath.addAll(index, tempPath);
                        }
                    }
                    allSeen = tmp;
                }while (!allSeen);
                return;
            }
            else {
                ECDFS(graph, neighbor, origin, currentPath);
            }

        }
    }

    public static class NotFoundException extends Exception{
        public NotFoundException(){
            super("Euler Circuit Not Found");
        }
    }

}

测试:

输入:

unDirectedEdgesnk
{{1,3}, {1,4}, {2,3}, {2,8}, {3,4}, {3,6}, {3,7},{3,9}, {4,5}, {4,7}, {4,10}, {4,11}, {5,10}, {6,9}, {7,9},{7,10}, {8,9}, {9,10}, {9,12}, {10,11}, {10,12}}125
{{1,3},{1,2},{3,4}, {2,3}, {2,4}};44

预计输出:
某一条从节点5开始节点5结束的欧拉回路

Euler Circuit Not Found

实际输出:
[5, 4, 1, 3, 2, 8, 9, 10, 12, 9, 3, 4, 7, 3, 6, 9, 7, 10, 4, 11, 10, 5]

Euler Circuit Not Found

测试通过


你可能还敢兴趣的图论算法(均附Java实现代码):

package hamierton; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Random; public class EularCircuit { public EularCircuit() { } public static void main(String[] args) { // System.out.println("please input n:"); // BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = 4; try { // n = Integer.parseInt(br.readLine()); } catch (Exception ex) { return; } try { Graph g = new Graph(n); g.printg(); g.circuit(); } catch (Exception e) { System.out.println(e.toString()); e.printStackTrace(); return; } } } class Node { private static int count = 0; private String name; private ArrayList adjacencyList; private boolean visited =false; public Node() { name = "node " + count; adjacencyList = new ArrayList(); } public Node(String name) { this.name = name; adjacencyList = new ArrayList(); } public boolean isAllVisited() { for (int i = 0; i < adjacencyList.size(); i++) { SNode sn = (SNode) adjacencyList.get(i); if (sn.visited == false) { return false; } } return true; } public boolean isvisited(){ return visited; } public void setvisited(){ visited = true; } public int getAdjacencyCount() { return adjacencyList.size(); } public boolean contains(int i) { return this.adjacencyList.contains(new SNode(i)); } public void removeAdjacencyNode(int i) { this.adjacencyList.remove(new SNode(i)); } public void addAdjacencyNode(int i) { this.adjacencyList.add(new SNode(i)); } public SNode getAdjacencyNode(int i) { return (SNode) this.adjacencyList.get(i); } public SNode getAdjacencyNodeEX(int i_ref) { for (int i = 0; i < this.getAdjacencyCount(); i++) { if (getAdjacencyNode(i).index == i_ref) { return getAdjacencyNode(i); } } return null; } public String toString() { return this.name; } } class SNode { public boolean visited = false; public int index = 0; public SNode(int index) { this.index = index; } public boolean equals(Object o) { if (((SNode) o).index == this.index) { return true; } return false; } public String toString() { return "adjacency " + index; } } class Graph { private ArrayList nodeList; private ArrayList path; private int count; public Graph(int n) throws Exception { this.count = n; nodeList = new ArrayList(count); ginit(); } public void circuit() { path = new ArrayList(); int top = 0; int k = 0; path.add(new Integer(0)); while (true) { int i, j; i = top; ArrayList path1 = new ArrayList(); path1.add(new Integer(top)); while (true) { Node node = (Node) nodeList.get(i); for (j = 0; j = path.size()) { break; } top = ((Integer) path.get(k)).intValue(); } for (int z = 0; z < path.size(); z++) { System.out.print(path.get(z).toString() + " "); } } private void ginit() { int i; for (i = 0; i < 4; i++) { nodeList.add(new Node("node" + i)); } ((Node)nodeList.get(0)).addAdjacencyNode(3); ((Node)nodeList.get(1)).addAdjacencyNode(0); ((Node)nodeList.get(2)).addAdjacencyNode(1); ((Node)nodeList.get(3)).addAdjacencyNode(2); // ((Node)nodeList.get(0)).addAdjacencyNode(3); // ((Node)nodeList.get(1)).addAdjacencyNode(0); // ((Node)nodeList.get(2)).addAdjacencyNode(1); // ((Node)nodeList.get(3)).addAdjacencyNode(2); // for (i = 0; i < n; i++) { // nodeList.add(new Node("node" + i)); // } // ArrayList linked = new ArrayList(); // linked.add(new Integer(0)); // Random rand = new Random(); // // for (i = 1; i = (linked.size() - 1 > 6 ? 6 // : linked.size() - 1)) { // continue; // } else { // i--; // } // node.addAdjacencyNode(randint); // Node randnode = (Node) nodeList.get(randint); // randnode.addAdjacencyNode(top); // break; // } // } // } // // for (i = 0; i < this.count - 1; i++) { // Node node = (Node) nodeList.get(i); // if (node.getAdjacencyCount() % 2 != 0) { // int j = 0; // for (j = i + 1; j < this.count; j++) { // Node nn = (Node) nodeList.get(j); // if (nn.getAdjacencyCount() % 2 != 0) { // if (node.contains(j)) { // // if (nn.getAdjacencyCount() != 1 // && node.getAdjacencyCount() != 1) { // node.removeAdjacencyNode(j); // nn.removeAdjacencyNode(i); // break; // } else { // continue; // } // } else { // // node.addAdjacencyNode(j); // nn.addAdjacencyNode(i); // break; // } // } // } // // if (j == this.count) { // int k; // Node nk = null; // for (k = i + 1; k < this.count; k++) { // // nk = (Node) nodeList.get(k); // if (nk.getAdjacencyCount() % 2 != 0) { // break; // } // } // int kk = k; // for (k = 0; k < i; k++) { // Node n1 = (Node) nodeList.get(k); // if (!n1.contains(kk) && !n1.contains(i)) { // n1.addAdjacencyNode(kk); // nk.addAdjacencyNode(k); // n1.addAdjacencyNode(i); // node.addAdjacencyNode(k); // break; // } // } // boolean retry = false; // // if (k == i) { // int vv; // for (vv = 0; vv < this.count; vv++) { // Node vn = (Node) nodeList.get(vv); // if (!vn.contains(i) && i != vv) { // vn.addAdjacencyNode(i); // node.addAdjacencyNode(vv); // retry = true; // break; // } // } // if (vv == count) { // for (vv = 0; vv 1) { // vnn.removeAdjacencyNode(i); // node.removeAdjacencyNode(vv); // retry = true; // break; // } // } // } // } // if (retry) { // i = -1; // } // } // } // // } // return this.isEularG(); } public boolean isEularG() { boolean isEular = true; for (int i = 0; i < this.count; i++) { Node n = (Node) nodeList.get(i); if (n.getAdjacencyCount() % 2 != 0) { isEular = false; break; } } return isEular; } public void printg() { for (int i = 0; i < this.count; i++) { Node n = (Node) nodeList.get(i); System.out.print(n.toString() + " "); for (int j = 0; j < n.getAdjacencyCount(); j++) { System.out.print(n.getAdjacencyNode(j).toString() + " "); } System.out.println(); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hack Rabbit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值