数据结构与算法--符号图

数据结构与算法--符号图

为了计算简单,传统的图中,都是使用整数来表示顶点,这样难免会有点抽象,不能直接反映各个顶点代表的信息。在实际生活中,使用图时一般会建立一个一一对应的关系表,如下

顶点 0 1 2
信息 北京 上海 深圳

图的处理过程中,比如说到处理顶点2,可能要去查看上表,才能知道说的是“深圳”。在典型的应用中,图都是通过文件或者网页定义的,更多的是用字符串而非整数,使用字符串能更加直观地反映各个顶点的信息。为了使得通过字符串能快速找到对应的顶点,会用到符号表进行查找,与图结合起来,这种数据结构被称为符号图

对于符号图的定义,有如下约定:

  • 顶点名是字符串
  • 输入中的每一行表示一组边的集合,每一行的第一个顶点和该行之后的所有顶点都相连。比如下面这个二维数组,里面每一个一维数组都是一组边的集合,一维数组中第一个顶点(如“产品D”),和其后的每一个顶点都相连(如与“产品D”对应的"经销商1",和"经销商6")
String[][] edges = {{"产品A", "经销商1", "经销商3", "经销商4", "经销商6"},
        {"产品B", "经销商1", "经销商2", "经销商3", "经销商5", "经销商6"},
        {"产品C", "经销商1", "经销商3", "经销商4", "经销商5", "经销商6"},
        {"产品D, "经销商1", "经销商6"}};

符号图的核心还是图,内部数据结构中顶点还是用整数表示。只不过里面加入了符号表的功能,使得整数顶点和字符串形成映射,可以方便地:

  • 通过整数索引,查找对应的字符串
  • 通过字符串,查找对应的顶点(整数表示)

基于此,实现符号图如下。

package Chap7;

import Chap8.BST;

public class SymbolUnDiGraph {
    // 键是顶点字符串,值是顶点
    private BST<String, Integer> st; // 符号名 -> 顶点索引
    private String[] keys; // 顶点索引 -> 符号名
    private UndiGraph<String> graph;

    public SymbolUnDiGraph(String[][] edges) {
        // 第一次遍历边集,建立符号表
        st = new BST<>();
        for (String[] infos : edges) {
            for (String info : infos) {
                // 重复的字符串不再分配顶点
                if (!st.contains(info)) {
                    st.put(info, st.size());
                }
            }
        }

        keys = new String[st.size()];
        for (String name : st.keys()) {
            keys[st.get(name)] = name;
        }

        // 第二次遍历边集
        // 构造图, 每一行的第一个顶点和该行的其他顶点相连
        graph = new UndiGraph<>(st.size());
        for (String[] infos : edges) {
            int v = st.get(infos[0]);
            for (int i = 1; i < infos.length; i++) {
                graph.addEdge(v, st.get(infos[i]));
            }
        }

    }

    public boolean contains(String name) {
        return st.contains(name);
    }

    public int indexOf(String name) {
        return st.get(name);
    }

    public String nameOf(int v) {
        return keys[v];
    }

    public UndiGraph<String> graph() {
        return graph;
    }

    public static void main(String[] args) {
        String[][] edges = {{"产品A", "经销商1", "经销商3", "经销商4", "经销商6"},
                {"产品B", "经销商1", "经销商2", "经销商3", "经销商5", "经销商6"},
                {"产品C", "经销商1", "经销商3", "经销商4", "经销商5", "经销商6"},
                {"产品D", "经销商1", "经销商6"}};

        SymbolUnDiGraph sg = new SymbolUnDiGraph(edges);
        UndiGraph<String> graph = sg.graph();
        System.out.println("经销商4有经营下面几种产品");
        for (int w : graph.adj(sg.indexOf("经销商4"))) {
            System.out.print(sg.nameOf(w) + " ");
        }
        System.out.println();

        System.out.println("产品C在下面几个经销商有售");
        for (int w : graph.adj(sg.indexOf("产品C"))) {
            System.out.print(sg.nameOf(w) + " ");
        }
        System.out.println();
    }
}

上面的代码会打印如下信息

经销商4有经营下面几种产品
产品A 产品C 
产品C在下面几个经销商有售
经销商1 经销商3 经销商4 经销商5 经销商6 

符号表的实现采用基于二分查找的有序数组。即上面的BST<String, Integer> st,可以通过字符串快速找到与之对应的顶点,这通过indexOf(String name)实现;

另外建立了一个String[]数组,可以通过整数表示的顶点获取该顶点的信息,这通过nameOf(int v)实现。contains方法可以直接用字符串判断某个顶点是否在图中。graph方法返回边集合定义的无向图。

从上面的代码可以看出,我们全程没有和整数打交道,实际上这些工作内部的数据结构已经帮我们做好了。代码中总共两次遍历边集,第一次是构造符号表,建立了顶点和字符串的关系。核心是下面这句,他将每一个不重复的字符串都放入符号表,与之对应的整数顶点直接使用符号表的长度这个值。(比如第一个被放入符号表的顶点为0,以此类推。)

// 重复的字符串不再分配顶点
if (!st.contains(info)) {
  st.put(info, st.size());
}

之后通过反向索引得到String[] keys,下图清晰得剖析了符号图的数据结构

image

符号图当然可以处理有向图,只需将图的数据类型换成有向图即可,其余代码都不改变!

package Chap7;

import Chap8.BST;

/**
 * 针对有向图的符号图,使用字符串表示顶点
 */
public class SymbolDiGraph {
    // 键是顶点字符串,值是顶点
    private BST<String, Integer> st; // 符号名 -> 顶点索引
    private String[] keys; // 顶点索引 -> 符号名
    private DiGraph<String> graph;

    public SymbolDiGraph(String[][] edges) {
        st = new BST<>();
        for (String[] infos : edges) {
            for (String info : infos) {
                // 重复的字符串不再分配顶点
                if (!st.contains(info)) {
                    st.put(info, st.size());
                }
            }
        }

        keys = new String[st.size()];
        for (String name : st.keys()) {
            keys[st.get(name)] = name;
        }

        // 构造图, 每一行的第一个顶点和该行的其他顶点相连
        graph = new DiGraph<>(st.size());
        for (String[] infos : edges) {
            int v = st.get(infos[0]);
            for (int i = 1; i < infos.length; i++) {
                graph.addEdge(v, st.get(infos[i]));
            }
        }

    }

    public boolean contains(String name) {
        return st.contains(name);
    }

    public int indexOf(String name) {
        return st.get(name);
    }

    public String nameOf(int v) {
        return keys[v];
    }

    public DiGraph<String> graph() {
        return graph;
    }

    public static void main(String[] args) {
        String[][] edges = {{"郑州", "上海", "北京", "西安", "成都"},
                {"成都", "郑州", "北京", "西安"},
                {"上海", "北京"},
                {"西安", "成都", "郑州"}};

        SymbolDiGraph sg = new SymbolDiGraph(edges);
        DiGraph<String> graph = sg.graph();
        for (int w : graph.adj(sg.indexOf("西安"))) {
            System.out.print(sg.nameOf(w) + " ");
        }
        System.out.println();
    }
}


by @sunhaiyu

2017.11.12

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,200评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,526评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,321评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,601评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,446评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,345评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,753评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,405评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,712评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,743评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,529评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,369评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,770评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,026评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,301评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,732评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,927评论 2 336

推荐阅读更多精彩内容