• 算法7-2:图论接口


    本节介绍怎样在程序中表示一张图。


    顶点

    在程序中,顶点用整数表示就能够了。由于整数能够作为数组的下标,也能够作为哈希表的键。

    所以用整数是最方便的。



    当然。在一张图中可能会出现一些异常情况。比方自己连接自己,两个顶点之间存在多个边。这些异常情况也是要考虑的。



    接口


    为了表示一张图,就要创建专门的对象来保存图。

    这个对象起名叫做Graph好了。它的接口是以下这种。

    public class Graph {
        // 创建一个带有V个顶点的图
        Graph(int V);
     
        // 从输入流创建一张图。输入流的格式下文有介绍
        Graph(In in);
     
        // 在两个顶点之间创建一条边。
        void addEdge(int v, int w);
     
        // 获取一个顶点的邻居顶点
        Iterable<Integer> adj(int v);
     
        // 获取这张图中顶点的数量
        int V();
     
        // 获取这张图中边的数量
        int E();
     
        // 将这张图表示成一个字符串,用于显示
        String toString();
    }


    输入格式


    接口中提到了能够从一个输入流创建一张图。那么这个输入流的格式应该如何呢?首先第一行是一个整数。表示这个图顶点的数量。第二行是一个整数,表示边的数量,后面的每一行中都有两个整数。表示两个顶点之间有一条边。



    设施


    图论中有一些简单的操作,比方计算一个顶点的度(邻居节点数量),自连接的数量等。这些操作在后面的算法中都会用到。它们的代码例如以下:


    public static int degree(Graph G, int v) {
        int count = 0;
        for (Integer e : G.adj(v)) count++;
        return count;
    }
     
    public static int maxDegree(Graph G) {
        int max = 0;
        for (int v = 0; v < G.V(); v++) {
            int d = degree(G, v);
            if (d > max) max = d;
        }
        return max;
    }
     
    public static int averageDegree(Graph G) {
        return G.E() * 2 / G.V();
    }
     
    public static int numberOfSelfLoops(Graph G) {
        int count = 0;
        for (int v = 0; v < G.V(); v++) {
            for (int w : G.adj(v)) {
                if (v == w) {
                    count++;
                }
            }
        }
        return count;
    }


    数据结构

    图主要有两种表示方法,一种是邻接矩阵,一种是邻接表。同学们别被这种怪名字吓到。事实上它们的本质都是数组或者链表。


    邻接矩阵


    邻接矩阵就是一个非常大的二维数组a,它的维度和顶点数量同样。假设顶点数量是V,那么这个二维矩阵就是V×V大小。当中a[v][w]就表示了顶点v和顶点w是否相连,0表示没有连接。1表示相连。



    邻接表


    邻接表事实上就是一个数组。数组中的每一个元素都用来记录某个顶点有哪些邻居。



    邻接表表示法的代码例如以下:

    public class Graph {
        private List<Integer>[] adj;
     
        public Graph(int V) {
            adj = new LinkedList[V];
            for (int i = 0; i < V; i++) {
                adj[i] = new LinkedList<Integer>();
            }
        }
     
        public void addEdge(int v, int w) {
            adj[v].add(w);
            adj[w].add(v);
        }
     
        public Iterable<Integer> adj(int v) {
            return adj[v];
        }
     
        public int V() {
            return adj.length;
        }
     
        public int E() {
            int result = 0;
            for (List<Integer> each : adj) {
                result += each.size();
            }
            return result;
        }
     
        @Override
        public String toString() {
            String result = "";
            for (int i = 0; i < adj.length; i++) {
                result += i + ":";
                for (int v : this.adj(i)) {
                    result += " " + v;
                }
                result += "
    ";
            }
            return result;
        }
    }


    实际应用中一般使用最多的就是邻接表,由于一般的应用顶点多而边数少。假设用邻接矩阵,内存浪费会非常严重,因此实际应用中更加偏向于使用邻接表。


  • 相关阅读:
    机器学习入门-贝叶斯垃圾邮件过滤(原理)
    机器学习入门-贝叶斯拼写纠错实例
    机器学习入门-贝叶斯算法(原理)
    机器学习入门-集成算法(bagging, boosting, stacking)
    高并发网站技术架构
    Nginx教程
    Shell脚本部分语法
    关于文本处理sort-cut-wc详解
    vim操作命令
    修改Linux基本配置
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7076516.html
Copyright © 2020-2023  润新知