0110符号图-无向图-数据结构和算法(Java)

1 概述

在典型应用中,图都是通过文件或者网页定义的,使用的是字符串而非整数来表示和指代顶点。为了适应这样的应用,我们定义了拥有一下性质的格式:

  • 顶点名为字符串;
  • 用指定的分隔符来隔开顶点;
  • 每一行都表示一组边的集合,每一条边都连接着这一行的第一个名称表示的顶点和其他名称所表示的顶点;
  • 顶点总数V和边的总数E都是隐式定义的。

图1-1如下所示:在这里插入图片描述

该图表示一个小型运输系统模型,其中每个顶点是美国机场的代码,连接它们的边则表示顶点之间的航线。

图1-2如下所示:

在这里插入图片描述

上图为互联网电影数据库,文件每一行列出了一个电影名以及出演该部电影的一系列演员。电影和演员都是顶点,而邻接表中的每一条边都将电影和它的表演者联系起来,这是一副二分图。

2 API

public classSymbolGraph
SymbolGraph(String filename, String delm)根据filename指定的文件构造图,使用delm分隔顶点
public booleancontains(String key)key是一个顶点吗
intindexOf(String key)key的索引
public StringnameOf(int v)索引v的顶点名
GraphG()隐藏的Graph对象

3 实现

package com.gaogzhen.datastructure.graph.undirected;

import edu.princeton.cs.algs4.Graph;
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.ST;

/**
 * 符号图
 * @author: Administrator
 * @createTime: 2023/03/14 19:59
 */
public class SymbolGraph {
    /**
     * 符号表
     * 符号名-索引
     */
    private ST<String, Integer> st;

    /**
     * 反向索引数组
     * 索引->符号名
     */
    private String[] keys;

    /**
     * 无向图
     */
    private Graph graph;

    /**
     * 文件读入数据构建无向图
     * @param filename  文件路径
     * @param delimiter 顶点分隔符
     */
    public SymbolGraph(String filename, String delimiter) {
        // 构建符号表
        st = new ST<>();
        In in = new In(filename);
        while (!in.isEmpty()) {
            String[] s = in.readLine().split(delimiter);
            for (int i = 0; i < s.length; i++) {
                if (!st.contains(s[i])) {
                    st.put(s[i], st.size());
                }
            }
        }
        // 初始化反向索引数组keys
        keys = new String[st.size()];
        for (String name : st.keys()) {
            keys[st.get(name)] = name;
        }
        // 构建无向图
        graph = new Graph(st.size());
        in = new In(filename);
        while (in.hasNextLine()) {
            String[] a = in.readLine().split(delimiter);
            int v = st.get(a[0]);
            for (int i = 1; i < a.length; i++) {
                int w = st.get(a[i]);
                graph.addEdge(v, w);
            }
        }
    }

    /**
     * 是否包含某个顶点
     * @param s   给定顶点
     * @return {@code true} 如果顶点 {@code s}在符号表中;否则 {@code false}
     */
    public boolean contains(String s) { return st.contains(s);}

    /**
     * 返回顶点名称对应的索引
     * @param s 顶点名称
     * @return 顶点名称 {@code s} 对应的索引(0~V-1)
     */
    public int indexOf(String s) {
        return st.get(s);
    }

    /**
     * 返回索引{@code v}对应的顶点名称
     * @param v 顶点索引(0~V-1)
     * @return  索引 {@code v} 对应的顶点名称
     */
    public String nameOf(int v) {
        validateVertex(v);
        return keys[v];
    }

    /**
     * 返回该符号图关联的无向图
     * @return  该符号图关联的无向图
     */
    public Graph graph() {
        return graph;
    }

    /**
     * 校验顶点 {@code v} 是否合法
     * @param v 顶点v
     */
    private void validateVertex(int v) {
        int c = graph.V();
        if (v < 0 || v >= c) {
            throw new IllegalArgumentException("顶点 " + v + " 不在 0~" + (c - 1) + " 之间");
        }
    }
}

符号表实现用到以下三种数据结构:

  • 符号表st,键的类型为String(顶点名),值的类型为Integer(索引);
  • 一个数组keys[],用做反向索引,保存每个顶点索引对应的顶点名称;
  • 一个Graph对象graph,它使用索引表示定的名称。

SymbolGraph会遍历2遍数据来构建上述数据结构。因为key[]和graph初始化需要用到顶点总数V,所以第一遍先构建符号表st,通过st.size()获取顶点总数。

典型应用中,不方便指定顶点总数V和边总数E,使用SymbolGraph方便在文件中增减数据。

4 测试

测试代码:

public static void testSymbolGraph() {
    SymbolGraph symbolGraph = new SymbolGraph("H:\\gaogzhen\\java\\projects\\algorithm\\asserts\\routes.txt", " ");
    System.out.println(symbolGraph.contains("ORD"));
    System.out.println(symbolGraph.indexOf("ORD"));
    System.out.println(symbolGraph.nameOf(3));
    System.out.println(symbolGraph.graph());
}

测试结果:

true
2
DEN
10 vertices, 18 edges 
0: 2 7 1 
1: 4 7 0 
2: 7 0 6 5 4 3 
3: 9 6 2 
4: 1 5 7 2 
5: 4 2 6 
6: 9 8 3 2 5 
7: 1 2 4 0 
8: 9 6 
9: 6 8 3 

演员电影图自己测试。

5 间隔度数

图处理一个经典问题就是,找到一个社交网络中两个人间隔的度数。以电影-演员图为例,kevin Bacon是一个活跃演员,演过很多电影,找出演员Kevin Bacon数。Kevin Bacon数:Bacon本人为0,所有和Bacon演过同一部电影的人的值为1,所有(除了Kevin Bacon)和Kevin Bacon数为1的演员演过同一部电影的其他演员值为2,依次类推。

实现代码:

package com.gaogzhen.datastructure.graph.undirected;

import edu.princeton.cs.algs4.StdIn;

/**
 * 无向图间隔度数
 * @author: Administrator
 * @createTime: 2023/03/14 21:17
 */
public class DegreesOfSeparation {
    public static void degreesOfSeparation(String filename, String delimiter, String source) {
        // 构建符号图
        SymbolGraph symbolGraph = new SymbolGraph(filename, delimiter);
        if (!symbolGraph.contains(source)) {
            System.out.println(source + " 不在数据库中");
            return;
        }
        // 符号图和起始顶点构建最短路径
        BreadthFirstDirectedPaths bfs = new BreadthFirstDirectedPaths(symbolGraph.graph(), symbolGraph.indexOf(source));

        while (!StdIn.isEmpty()) {
            String sink = StdIn.readLine();
            if (symbolGraph.contains(sink)) {
                int s = symbolGraph.indexOf(sink);
                if (bfs.hasPathTo(s)) {
                    for (Integer v : bfs.pathTo(s)) {
                        System.out.println("\t" + symbolGraph.nameOf(v));
                    }
                } else {
                    System.out.println(sink + " 与 " + source + " 不相连");
                }
            } else {
                System.out.println(sink + " 不在数据库中");
            }
        }
    }
}

测试代码:

    public static void testDegreesOfSeparate() {
        String filename = "H:\\gaogzhen\\java\\projects\\algorithm\\asserts\\movies.txt";
        String delimiter = "/";
        String source = "Bacon, Kevin";
        DegreesOfSeparation.degreesOfSeparation(filename, delimiter, source);
    }

测试结果:

xxx
xxx 不在数据库中
Kidman, Nicole
	Bacon, Kevin
	JFK (1991)
	Sutherland, Donald (I)
	Cold Mountain (2003)
	Kidman, Nicole
Aronson, Steve
	Bacon, Kevin
	JFK (1991)
	X, Malcolm
	Malcolm X (1992)
	Aronson, Steve

间隔度数的理论在计算机网络的设计以及理解各个科学领域中的自然网络中都能起到重要的作用。

后记

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

❓QQ:806797785

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

参考链接:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

gaog2zh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值