图的基本概念和遍历

1. 定义

图: 由顶点(vertex) 和边(edge) 组成, 通常表示为G = (V, E)

G 表示是一个图, V 是顶点集合, E 是边的集合

顶点集合V 有穷且非空

任意两个顶点之间都可以用边来表示他们之间的关系, 边集E 可以是空的

例如:

其他如: 社交网络, 之间的关系, 地图导航, 两地之间的路, 游戏开发等

图的应用

2. 有向图(Directed Graph)

有向图的边是有明确方向的

有向图

有向无环图(Directed Acyclic Graph), DAG

如果一个有向图, 从任一顶点触发无法经过若干条边回到该顶点, 那么它就是一个有向无环图

有向无环图

3. 出度, 入度

适用于有向图

出度(Out-degree)

一个顶点的出度x, 是指有x 条边以该顶点为起点

入度(In-degree)

一个顶点的入度为x, 是指有x 条边以该顶点为终点

4. 无向图(Undirected Graph)

无向图的边是无方向的

无向图

效果类似于有向图

有向图

5. 混合图(Mixed Graph)

混合图的边可能是无向的, 可能是有向的

混合图

6. 简单图, 多重图

平行边

在无向图中, 关联一对顶点的无向边如果多于一条, 则称这些边为平行边

有向图中, 关联一对顶点的有向边如果多于一条, 并且他们的方向相同, 则称这些边为平行边

多重图(Multigraph)

有平行边或者有自环的图

简单图(Simple Graph)

既没有平行边也没有自环的图

多重简单图

7. 无向完全图(Undirected Complete Graph)

无向完全图的任意两个顶点之间都存在边

n 个顶点的无向完全图有n(n - 1)/2 条边

无向完全图

8. 有向完全图(Directed Complete Graph)

有向完全图的任意两个顶点之间都存在方向相反的两条边

n 个顶点的有向完全图有n(n - 1) 条边

有向完全图

稠密图(Dense Graph): 边数接近于或等于完全图

稀疏图(Sparse Graph): 边数远远少于完全图

9. 有权图(Weighted Graph)

有权图的边可以有权值(Weight)

有权图

10. 连通图(Connected Graph)

如果顶点x 和y 之间存在可相互抵达的路径(直接或间接的路径), 则称x 和y 是联通的

如果无向图G 中任意两个顶点都是联通的, 则称G 为连通图

连通图

11. 连通分量(Connected Component)

连通分量: 无向图的极大连通子图

连通图只有一个连通分量, 即其自身. 非连通的无向图有多个连通分量

连通分量

12. 强连通图(Strongly Connected Graph)

如果有向图G 中任意两个顶点都是连通的, 则称G 为强连通图

强连通图

13. 强连通分量(Strongly Connected Component Graph)

强连通分量: 有向图的极大强连通子图

强连通图只有一个强连通分量, 即其自身. 非强连通的有向图有多个强连通分量

强连通分量

12. 图的实现方式:

邻接矩阵(Adjacency Matrix)

邻接表(Adjacency List)

邻接矩阵

存储方式

  • 一维数组存放顶点信息
  • 二维数组存放边的信息

邻接矩阵比较适合稠密图

邻接矩阵1
邻接矩阵2

邻接表(Adjacency List)

邻接表1
邻接表2

13. 图的遍历搜索

图的接口定义

    public abstract int edgesSize();
    public abstract int verticesSize();
    
    public abstract void addVertex(V v);
    public abstract void addEdge(V from, V to);
    public abstract void addEdge(V from, V to, E weight);
    
    public abstract void removeVertex(V v);
    public abstract void removeEdge(V from, V to);
    
    public abstract void bfs(V begin, VertexVisitor<V> visitor);
    public abstract void dfs(V begin, VertexVisitor<V> visitor);

    public interface VertexVisitor<V> {
        boolean visit(V v);
    }

图的遍历

从图中某一顶点访问其他顶点, 且每个顶点尽被访问一次

  • 广度优先搜索(Breadth First Search) BFS, 又称宽度优先搜索, 横向优先搜索
  • 深度优先搜索(Depth First Search), DFS

广度优先搜索

二叉树的层序遍历也是一种广度优先搜索

广度优先搜索1
广度优先搜索2

思路

广度优先搜索思路
    @Override
    public void bfs(V begin, VertexVisitor<V> visitor) {
        Vertex<V, E> beginVertex = vertices.get(begin);
        if (beginVertex == null) return;
        // 已经遍历过的顶点
        Set<Vertex<V, E>> visitedVertices = new HashSet<>();
       // 使用队列, 存储即将访问的顶点
        Queue<Vertex<V, E>> queue = new LinkedList<>();
       // 出队
        queue.offer(beginVertex);
        visitedVertices.add(beginVertex);
        while (!queue.isEmpty()) {
            Vertex<V, E> vertex = queue.poll();
//          System.out.println(vertex.value);
         // 遍历停止
            if (visitor.visit(vertex.value)) return;
            for (Edge<V, E> edge : vertex.outEdges) {
             // 已经访问过, 直接跳过
                if (visitedVertices.contains(edge.to)) continue;
                queue.offer(edge.to);
                visitedVertices.add(edge.to);
            }
        }
    }

深度优先搜索

二叉树的前序遍历也是一种深度优先搜索

广度优先搜索
    // 递归实现深度优先搜索
    public void dfs(V begin) {
        Vertex<V, E> beginVertex = vertices.get(begin);
        if (beginVertex == null) return;
        
        dfs(beginVertex, new HashSet<>());
    }
    
    // 迭代实现深度优先遍历
    @Override
    public void dfs(V begin, VertexVisitor<V> visitor) {
        if (visitor == null) return;
        Vertex<V, E> beginVertex = vertices.get(begin);
        if (beginVertex == null) return;
        
        Set<Vertex<V, E>> visitedVertices = new HashSet<>();
        Stack<Vertex<V, E>> stack = new Stack<>();
        
        // 先访问起点
        stack.push(beginVertex);
        visitedVertices.add(beginVertex);
//      System.out.println(beginVertex.value);
        if (visitor.visit(begin)) return;
        while (!stack.isEmpty()) {
            Vertex<V, E> vertex = stack.pop();
            
            for (Edge<V, E> edge : vertex.outEdges) {
                if (visitedVertices.contains(edge.to)) continue;
                stack.push(edge.from);
                stack.push(edge.to);
                visitedVertices.add(edge.to);
//              System.out.println(edge.to.value);
                if (visitor.visit(edge.to.value)) return;
                break;
            }   
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容