zoukankan      html  css  js  c++  java
  • 【算法】欧拉图,欧拉回路,Eular Circuit,随机生成欧拉图,搜索欧拉回路

    背景:图论起源于18世纪,1736年瑞士数学家欧拉(Eular)发表了图论的第一篇论文“哥尼斯堡七桥问题”。在当时的哥尼斯堡城有一条横贯全市的普雷格尔河,河中的两个岛与两岸用七座桥联结起来,见图(1)。当时那里的居民热衷于一个难题:游人怎样不重复地走遍七桥,最后回到出发点。

    为了解决这个问题,欧拉用ABCD四个字母代替陆地,作为4个顶点,将联结两块陆地的桥用相应的线段表示,如图(2),于是哥尼斯堡七桥问题就变成了图(2)中,是否存在经过每条边一次且仅一次,经过所有的顶点的回路问题了。欧拉在论文中指出,这样的回路是不存在的。

    1 定义

      欧拉通路 (欧拉迹)——通过图中每条边一次且仅一次,并且过每一顶点的通路。

      欧拉回路 (欧拉闭迹)——通过图中每条边一次且仅一次,并且过每一顶点的回路。

      欧拉图——存在欧拉回路的图。

    2 无向图是否具有欧拉通路或回路的判定

      G有欧拉通路的充分必要条件为:G 连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点)。

      G有欧拉回路(G为欧拉图):G连通,G中均为偶度顶点。

    3 有向图是否具有欧拉通路或回路的判定

      D有欧拉通路:D连通,除两个顶点外,其余顶点的入度均等于出度,这两个特殊的顶点中,一个顶点的入度比出度大1,另一个顶点的入度比出度小1。

      D有欧拉回路(D为欧拉图):D连通,D中所有顶点的入度等于出度。

     

    以下是我的欧拉回路的实现,包括随机生成欧拉图和搜索欧拉回路。

     

    欧拉回路
    package eulercircuit;

    /*
    filename:EularCircuit.java
    author:gaoshangshang
    version:0.9
    date:2008-10-22
    run environment:jdk 1.4
    description: 随机生成欧拉图,稀疏图 邻接表表示 复杂度O(|E|+|V|)
    算法描述:先对欧拉图执行一次深度优先搜索,找到第一个回路,此时,
    仍有部分边未被访问,则找出有尚未访问的边的路径上的第一个顶点,
    执行另外一次DFS,得到另外一个回路,然后把第二个回路拼接在第一
    个回路上,继续该过程,直到所有的边都被遍历。
    */

    import java.io.*;
    import java.util.*;

    publicclass EularCircuit {
    public EularCircuit() {
    }

    publicstaticvoid 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 {
    //该变量纯粹为了便于给顶点默认命名,在此程序中无特殊用途
    privatestaticint count =0;
    private String name;
    //该顶点的邻接表,里面存储的是SNode,内含该顶点邻接的顶点在图的顶点列表中的index和该边是否被访问
    private ArrayList adjacencyList;

    public Node() {
    name
    ="node "+ count;
    adjacencyList
    =new ArrayList();
    }

    public Node(String name) {
    this.name = name;
    adjacencyList
    =new ArrayList();
    }

    //该函数用于测试本顶点的所有边是否都已经被访问,本程序未用
    publicboolean isAllVisited() {
    for (int i =0; i < adjacencyList.size(); i++) {
    SNode sn
    = (SNode) adjacencyList.get(i);
    if (sn.visited ==false) {
    returnfalse;
    }
    }
    returntrue;
    }

    //返回该结点的邻接点的个数
    publicint getAdjacencyCount() {
    return adjacencyList.size();
    }

    //用于测试图中的index为i的顶点是否在本顶点的邻接表中
    publicboolean contains(int i) {
    returnthis.adjacencyList.contains(new SNode(i));
    }

    //删除本顶点的一个邻接点,该点的index为i
    publicvoid removeAdjacencyNode(int i) {
    this.adjacencyList.remove(new SNode(i));
    }

    publicvoid addAdjacencyNode(int i) { //i为邻接顶点在图顶点列表中的index。
    this.adjacencyList.add(new SNode(i));
    }

    //返回邻接表中的第i个邻接点
    public SNode getAdjacencyNode(int i) {
    return (SNode)this.adjacencyList.get(i);
    }

    //返回邻接表中一个SNode,该SNode的属性“index”为i_ref
    public SNode getAdjacencyNodeEX(int i_ref) {
    for (int i =0; i <this.getAdjacencyCount(); i++) {
    if (getAdjacencyNode(i).index == i_ref) {
    return getAdjacencyNode(i);
    }
    }
    returnnull;
    }

    public String toString() {
    returnthis.name;
    }
    }

    //simple node,主要用于邻接表中,包含index(该点在图的nodelist中index)和visited(该边是否访问过)
    class SNode {
    publicboolean visited =false;
    publicint index =0;

    public SNode(int index) {
    this.index = index;
    }

    //必须重载
    publicboolean equals(Object o) {
    if ( ( (SNode) o).index ==this.index) {
    returntrue;
    }
    returnfalse;
    }

    public String toString() {
    return"adjacency "+ index;
    }
    }

    //欧拉图,随机生成,如果生成失败,则抛出异常。
    class Graph {
    private ArrayList nodeList; //顶点列表
    private ArrayList path; //欧拉回路
    privateint count; //顶点数

    public Graph(int n) throws Exception {
    this.count = n;
    nodeList
    =new ArrayList(count);
    if (!ginit(count)) {
    thrownew Exception("sorry,a failure,retry please");
    }
    }

    //搜索欧拉回路,算法描述见文件顶部注释
    publicvoid circuit() {
    path
    =new ArrayList();
    int top =0; //当前回游的起点
    int k =0; //当前回游的起点在path中的index
    path.add(new Integer(0));
    while (true) {
    int i, j;
    //int init=0;
    i = top; //i为一次回游的当前点
    ArrayList path1 =new ArrayList();
    path1.add(
    new Integer(top));
    while (true) { //执行一遍回路遍历,但不一定遍历所有的边
    Node node = (Node) nodeList.get(i);
    for (j =0; j <this.count; j++) {
    if (node.contains(j) && node.getAdjacencyNodeEX(j).visited ==false) {
    path1.add(
    new Integer(j));
    node.getAdjacencyNodeEX(j).visited
    =true;
    ( (Node) nodeList.get(j)).getAdjacencyNodeEX(i).visited
    =true;
    i
    = j;
    break;
    }
    }
    if (i == top) {
    break;
    }
    }
    path.remove(k);
    path.addAll(k, path1);
    k
    ++;
    if (k >= path.size()) {
    break;
    }
    top
    = ( (Integer) path.get(k)).intValue();
    }
    for (int z =0; z < path.size(); z++) {
    System.out.print(path.get(z).toString()
    +"");
    }
    }

    //随机生成欧拉图
    privateboolean ginit(int n) {
    int i;
    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 < n; i++) {
    int size = linked.size(); //取得连通集合里面的最后进入的顶点index
    int top = ( (Integer) (linked.get(size -1))).intValue();
    Node node
    = (Node) (nodeList.get(top)); //根据top取得该node
    while (true) {
    int randint = rand.nextInt(n);
    if (randint == top) {
    continue;
    }
    if (node.contains(randint)) {
    continue;
    }
    else {
    if (!linked.contains(new Integer(randint))) {
    linked.add(
    new Integer(randint));
    }
    elseif (node.getAdjacencyCount() >=
    (linked.size()
    -1>6?6 : linked.size() -1)) {
    continue;
    }
    else {
    i
    --;
    }
    node.addAdjacencyNode(randint);
    Node randnode
    = (Node) nodeList.get(randint);
    randnode.addAdjacencyNode(top);
    break;
    }
    //end if
    } //end while
    } //end for
    //printg();//for test

    //使所有的顶点度数为偶数
    /*如果该顶点v1的度数为奇数,则从后面找一个度数也为奇数的点v2,
    1、如果v1和v2已经有边,则如果两个点的度数不为1,则去掉它们的连边,
    否则继续往后查找度数为奇数的点。
    2、如果v1和v2没有边,则在它们之间连边
    3、如果没有找到合适的点,则再找一度数为奇的点v3,和一个度数为偶的点v4,如果v4
    和v1,v3没有相连,则在v4和v1,v4和v3之间加边。
    4、如果仍然没有找到,4、1 则将该点与任意一个不相连的点之间加边,重新执行该“变偶”操作;
    4、2 如果找不到与该点不相连的点,则找一度数大于2的点,去除与该点的边,重新执行
    */
    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)) { //1、见上注释
    if (nn.getAdjacencyCount() !=1&& node.getAdjacencyCount() !=1) {
    node.removeAdjacencyNode(j);
    nn.removeAdjacencyNode(i);
    break;
    }
    else {
    continue;
    }
    }
    else { //2、见上注释
    node.addAdjacencyNode(j);
    nn.addAdjacencyNode(i);
    //i = j;
    break;
    }
    //end if
    } //end if
    } //end for
    if (j ==this.count) {
    int k;
    Node nk
    =null;
    for (k = i +1; k <this.count; k++) {//3、见上注释
    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) {//4、1 见上注释
    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) {//4、2 见上注释
    for (vv =0; vv < count; vv++) {
    Node vnn
    = (Node) nodeList.get(vv);
    if (vnn.getAdjacencyCount() >1) {
    vnn.removeAdjacencyNode(i);
    node.removeAdjacencyNode(vv);
    retry
    =true;
    break;
    }
    }
    }
    }
    if (retry) {
    i
    =-1;
    }
    }
    }
    //end if
    }
    returnthis.isEularG();
    }
    //end function

    //判断生成的图是否为欧拉图
    publicboolean 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;
    }

    publicvoid 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();
    }
    }
    }
  • 相关阅读:
    解题:AHOI 2005 航线规划
    解题:SCOI 2008 天平
    解题:SCOI 2014 方伯伯运椰子
    解题:APIO 2008 免费道路
    解题:USACO15JAN Grass Cownoisseur
    669. 换硬币(dp动态规划)
    8. 旋转字符串
    147. 水仙花数
    1131. 排列中的函数
    78. 最长公共前缀
  • 原文地址:https://www.cnblogs.com/gaojing/p/1318056.html
Copyright © 2011-2022 走看看