0102数据结构和图处理算法-无向图-数据结构和算法(Java)

1 数据类型

要处理各种图的算法,我们首先看一份定义了图的基本操作的API,如下表1-1所示:

public classGraph
Graph(int V)创建一个含有V个顶点但不含有边的图
Graph(In in)从标准输入流in读入一幅图
intV()顶点数
intE()边数
voidaddEdge(int v, int w)向图中添加一条边v-w
Interable<Integer>adj(int v)和v相邻的顶点集合
StringtoString()对象的字符串表示

2 图的表示方法

下一个图处理问题就是用那种方法(数据结构)来表示图并实现上述API,包含以下两个要求:

  • 它必须为为可能在应用中碰到的各种类型的图预留足够的空间;
  • Graph实例方法实现要快。

比较以下三种表示方法:

  • 邻接矩阵:使用一个V X X XV的布尔矩阵(二维数组)。当顶点v和顶点w有边相连,设置v行w列和w行v列为true(1),否则false。
    • 问题:空间数量级为 V 2 V^2 V2,当顶点有百万个时,显然不满足需求。
  • 边的数组:定义一个Edge类,它含有2个int型变量表示它所依附的两个顶点。
    • 问题:实现简单,但是要实现adj()需要检查图中所有的边。
  • 邻接表数组:使用一个以顶点为索引的列表(链表)数组,其中数组每个元素表示和该顶点相邻的顶点列表。

我们选择邻接表数组实现上述API,无向图及其邻接表数组示意图如下:

在这里插入图片描述

在这里插入图片描述

3 无向图的实现

源代码如下3-1所示:算法第四版源码

public class Graph {
    private static final String NEWLINE = System.getProperty("line.separator");

    private final int V;
    private int E;
    private Bag<Integer>[] adj;
    
    /**
     * 初始化含有V个顶点边为0的图
     *
     * @param  V 顶点数
     * @throws IllegalArgumentException if {@code V < 0}
     */
    public Graph(int V) {
        if (V < 0) throw new IllegalArgumentException("Number of vertices must be non-negative");
        this.V = V;
        this.E = 0;
        adj = (Bag<Integer>[]) new Bag[V];
        for (int v = 0; v < V; v++) {
            adj[v] = new Bag<Integer>();
        }
    }

    /**  
     * 从标准输入流in读入一个图
     *
     * @param  in the input stream
     * @throws IllegalArgumentException if {@code in} is {@code null}
     * @throws IllegalArgumentException if the endpoints of any edge are not in prescribed range
     * @throws IllegalArgumentException if the number of vertices or edges is negative
     * @throws IllegalArgumentException if the input stream is in the wrong format
     */
    public Graph(In in) {
        if (in == null) throw new IllegalArgumentException("argument is null");
        try {
            this.V = in.readInt();
            if (V < 0) throw new IllegalArgumentException("number of vertices in a Graph must be non-negative");
            adj = (Bag<Integer>[]) new Bag[V];
            for (int v = 0; v < V; v++) {
                adj[v] = new Bag<Integer>();
            }
            int E = in.readInt();
            if (E < 0) throw new IllegalArgumentException("number of edges in a Graph must be non-negative");
            for (int i = 0; i < E; i++) {
                int v = in.readInt();
                int w = in.readInt();
                validateVertex(v);
                validateVertex(w);
                addEdge(v, w); 
            }
        }
        catch (NoSuchElementException e) {
            throw new IllegalArgumentException("invalid input format in Graph constructor", e);
        }
    }


    /**
     * Initializes a new graph that is a deep copy of {@code G}.
     *
     * @param  G the graph to copy
     * @throws IllegalArgumentException if {@code G} is {@code null}
     */
    public Graph(Graph G) {
        this.V = G.V();
        this.E = G.E();
        if (V < 0) throw new IllegalArgumentException("Number of vertices must be non-negative");

        // update adjacency lists
        adj = (Bag<Integer>[]) new Bag[V];
        for (int v = 0; v < V; v++) {
            adj[v] = new Bag<Integer>();
        }

        for (int v = 0; v < G.V(); v++) {
            // reverse so that adjacency list is in same order as original
            Stack<Integer> reverse = new Stack<Integer>();
            for (int w : G.adj[v]) {
                reverse.push(w);
            }
            for (int w : reverse) {
                adj[v].add(w);
            }
        }
    }

    /**
     * 顶点数
     *
     * @return the number of vertices in this graph
     */
    public int V() {
        return V;
    }

    /**
     * 边数
     *
     * @return the number of edges in this graph
     */
    public int E() {
        return E;
    }

    // throw an IllegalArgumentException unless {@code 0 <= v < V}
    private void validateVertex(int v) {
        if (v < 0 || v >= V)
            throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
    }

    /**
     * 向无向图添加一条边v-w
     *
     * @param  v one vertex in the edge
     * @param  w the other vertex in the edge
     * @throws IllegalArgumentException unless both {@code 0 <= v < V} and {@code 0 <= w < V}
     */
    public void addEdge(int v, int w) {
        validateVertex(v);
        validateVertex(w);
        E++;
        adj[v].add(w);
        adj[w].add(v);
    }


    /**
     * 和顶点v相邻的所有顶点
     *
     * @param  v the vertex
     * @return the vertices adjacent to vertex {@code v}, as an iterable
     * @throws IllegalArgumentException unless {@code 0 <= v < V}
     */
    public Iterable<Integer> adj(int v) {
        validateVertex(v);
        return adj[v];
    }

    /**
     * 顶点v的度数
     *
     * @param  v the vertex
     * @return the degree of vertex {@code v}
     * @throws IllegalArgumentException unless {@code 0 <= v < V}
     */
    public int degree(int v) {
        validateVertex(v);
        return adj[v].size();
    }


    /**
     * 图的字符串表示
     *
     * @return the number of vertices <em>V</em>, followed by the number of edges <em>E</em>,
     *         followed by the <em>V</em> adjacency lists
     */
    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append(V + " vertices, " + E + " edges " + NEWLINE);
        for (int v = 0; v < V; v++) {
            s.append(v + ": ");
            for (int w : adj[v]) {
                s.append(w + " ");
            }
            s.append(NEWLINE);
        }
        return s.toString();
    }

}

非稠密图的标准表示就是邻接表。我们用一个以顶点为索引的链表数组表示,链表使用Bag包数据类型实现。每个元素存储该顶点相邻的顶点列表。实现特点如下:

  • 使用空间和V+E成正比
  • 添加一条边所需时间为常数
  • 遍历顶点v的所有相邻顶点和所需的时间和v的度数成正比

4 图处理算法的设计模式

因为我们会讨论大量关于图处理的算法,所以设计的首要目标是将图的表示和实现分离开来。我们会为每个任务(算法)创建一个相应的类,典型的用例程序会构造一幅图,作为构造函数的参数传递给实现了某个算法的类。

搜索与某个顶点的连通顶点算法Search API示例如下:

public classsearch
Search(Graph g, int s)找到和启点s联通的所有顶点
booleanmarked(int v)v和s是连通的吗
intcout()与s连通顶点总数

后记

如果小伙伴什么问题或者指教,欢迎交流。

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/algorithm

参考链接:

[1][美]Robert Sedgewich,[美]Kevin Wayne著;谢路云译.算法:第4版[M].北京:人民邮电出版社,2012.10

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

gaog2zh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值