图算法系列之无向图的数据结构

吐血整理程序员必读书单:https://github.com/silently9527/ProgrammerBooks

微信公众号:贝塔学Java

前言

从本篇开始我们将会一起来学习图相关的算法,图算有很多相当实用算法,比如:垃圾回收器的标记清除算法、地图上求路径的最短距离、拓扑排序等。在开始学习这些算法之前我们需要先来了解下图的基本定义,以及使用哪种数据结构来表示一张图,本篇我们先从无向图开始学习。

图的定义

图:是有一组顶点和一组能够将两个订单相连组成的。连接两个顶点的边没有方向,这种图称之为无向图。

image

图的术语

通过同一条边相连的两个顶点我们称这两个顶点相邻

某个顶点的度数即表示连接这个顶点的边的总数;如上图:顶点1的度数是3

一条边连接了一个顶点与其自身,我们称为自环

连接同一对顶点的边称为平行边

image

术语还有很多,暂时这里只列出本篇我们需要使用到的术语,后面有在使用到其他的术语再做解释,太多也不太容易记得住

如何表示出图

图用什么数据结构来表示主要参考两个要求:

  1. 在开发图的相关算法时,图的表示的数据结构是基础,所以这种数据结构效率的高
  2. 在实际的过程中图的大小不确定,可能会很大,所以需要预留出足够的空间

考虑了这两个要求之后大佬们提出以下三个方法来供选择:

  • 邻接矩阵
    键入有v个顶点的图,我们可以使用v乘以v的矩阵来表示,如果顶点v与w相连,那么把v行w列设置为true,这样就可以表示两个顶点相连,但是这个方式有个问题,如果遇到图很大,会造成空间的浪费。不满足第二点。其次这种方式没办法表示平行边
  • 边的数组
    可以定义一个表示的边对象,包含两个int属性表示顶点,但是如果需要找到某个顶点的相连顶点有哪些,我们就需要遍历一遍全部的边。这种数据结构的效率较差
  • 邻接表数组
    定义一个数组,数组的大小为顶点的个数,数据下标表示顶点,数组中每个元素都是一个链表对象(LinkedListQueue),链表中存放的值就是与该顶点相连的顶点。(LinkedListQueue我们已经在之前的文章中实现,可以参考文章《https://juejin.cn/post/6926685994347397127》)
image

无向图的API定义

public class Graph {
    public Graph(int V); //创建含有v个顶点不含边的图
    
    public int V(); //返回顶点的个数
    
    public int E(); //返回图中边的总数
    
    public void addEdge(int v, int w); //向图中添加一条边 v-W 
        
    public Iterable<Integer> adj(int v); //返回与v相邻的所有顶点
    
    public String toString(); //使用字符串打印出图的关系
}

无向图API的实现

要实现上面定义的API,我们需要三个成员变量,v表示图中顶点的个数,e表示图总共边的数据,LinkedListQueue的数组用来存储顶点v的相邻节点;

构造函数会初始化空的邻接表数组

因为是无向图,所以addEdge方法在向图中添加边既要添加一条v->w的边,有需要添加一条w->v的边

public class Graph {
    private final int v;
    private int e;
    private LinkedListQueue<Integer>[] adj;

    public Graph(int v) {
        this.v = v;
        this.adj = (LinkedListQueue<Integer>[]) new LinkedListQueue[v];
        for (int i = 0; i < v; i++) {
            this.adj[i] = new LinkedListQueue<>();
        }
    }

    public int V() {
        return v;
    }

    public int E() {
        return e;
    }

    public void addEdge(int v, int w) {
        this.adj[v].enqueue(w);
        this.adj[w].enqueue(v);
        this.e++;
    }

    public Iterable<Integer> adj(int v) {
        return this.adj[v];
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(v).append(" 个顶点,").append(e).append(" 条边\n");
        for (int i = 0; i < v; i++) {
            sb.append(i).append(": ");
            for (int j : this.adj[i]) {
                sb.append(j).append(" ");
            }
            sb.append("\n");
        }
        return sb.toString();
    }
}

图的常用工具方法

基于图数据结构的实现,我们可以提供一些工具方法

  1. 计算顶点v的度数
    顶点的度数就等于与之相连接顶点的个数
public static int degree(Graph graph, int v) {
    int degree = 0;
    for (int w : graph.adj(v)) {
        degree++;
    }
    return degree;
}
  1. 计算所有顶点的最大度数
public static int maxDegree(Graph graph) {
    int maxDegree = 0;
    for (int v = 0; v < graph.V(); v++) {
        int degree = degree(graph, v);
        if (maxDegree < degree) {
            maxDegree = degree;
        }
    }
    return maxDegree;
}
  1. 计算所有顶点的平均度数
    每条边都有两个顶点,所以图所有顶点的总度数是边的2倍
public static double avgDegree(Graph graph) {
    return 2.0 * graph.E() / graph.V();
}
  1. 计算图中的自环个数
    对于顶点v,如果v同时也出现了在v的邻接表中,那么表示v存在一个自环;由于是无向图,每条边都被记录了两次(如果不好理解可以把图的toString打印出来就可以理解了)
public static int numberOfSelfLoops(Graph graph) {
    int count = 0;
    for (int v = 0; v < graph.V(); v++) {
        for (int w : graph.adj(v)) {
            if (v == w) {
                count++;
            }
        }
    }
    return count / 2;
}

总结

本篇我们主要学习使用何种数据结构来表示一张图,以及基于这种数据结构实现了几个简单的工具方法,在下一篇我们将来学习图的第一个搜索算法 - 深度优先搜索


文中所有源码已放入到了github仓库:
https://github.com/silently9527/JavaCore

最后(点关注,不迷路)

文中或许会存在或多或少的不足、错误之处,有建议或者意见也非常欢迎大家在评论交流。

最后,写作不易,请不要白嫖我哟,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

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

推荐阅读更多精彩内容