无向图-DFS和BFS

本文介绍了无向图的概念,包括顶点、边、无向图的分类和表示方法,如邻接矩阵、边的数组和邻接表。接着详细探讨了深度优先遍历(DFS)和广度优先遍历(BFS),包括它们的遍历流程、应用,如联通分量个数、路径问题、环检测和二分图检测。最后对比了DFS与BFS的特点和应用场景。
摘要由CSDN通过智能技术生成

无向图

1 图

在这里插入图片描述

是由一组顶点和一组能够将两个顶点相连的边组成的。

图(Graph)结构是一种非线性的数据结构,图在实际生活中有很多例子,比如交通运输网,地铁网络,社交网络,计算机中的状态执行等等都可以抽象成图结构。图结构比树结构复杂的非线性结构。
在这里插入图片描述

图的构成

**1.顶点(vertex):**图中的数据元素

**2.边(edge):**图中连接这些顶点的线

所有的顶点构成一个顶点集合,所有的边构成边的集合,一个完整的图结构就是由顶点集合和边集合组成。图结构在数学上记为以下形式:

G=(V,E) 或者 G=(V(G),E(G))

两个核心要素:顶点和边,一个图可以没有边,但不能没有顶点。
在这里插入图片描述

图的定义和绘出的图像是无关的。

图的分类

在这里插入图片描述
好友关系:无向图(undirected graph) 一个图结构中,所有的边都没有方向性,那么这种图便称为无向图。

关注关系:有向图(directed graph) 一个图结构中,边是有方向性的,那么这种图就称为有向图。
在这里插入图片描述

有权图无权图

这里的可以理解成一个数值,就是说节点与节点之间这个边是否有一个数值与它对应,对于无权图来说这个边不需要具体的值。

对于有权图节点与节点之间的关系可能需要某个值来表示,比如这个数值能代表两个顶点间的距离,或者从一个顶点到另一个顶点的时间,所以这时候这个边的值就是代表着两个节点之间的关系,这种图被称为有权图

分类:

无向 有向
无权 无向无权图 有向无权图
有权 无向有权图 有向有权图

本次课程涉及到图为无权无向图

2 无向图

特殊的图:定义允许出现两种简单而特殊的情况,

**1.自环边(self-loop):**节点自身的边,自己指向自己。

**2.平行边(parallel-edges):**两个节点之间存在多个边相连接。
在这里插入图片描述
简单图:无自环、无平行边。

图术语介绍:

**① **相邻、度数、子图

  • 相邻:当两个顶点通过一条边相连时,我们称这两个顶点是相邻的, 并称这条边依附于这两个顶点。
  • 度数:某个顶点的度数即为依附于它的边的总数。
  • **子图:**是由一幅图的所有边的一个子集(以及它们所依附的所有顶点 )组成的图。

路径、环

  • 路径:是由边顺序连接的一系列顶点。简单路径是一条没有重复顶点的路径。
  • :是一条至少含有一条边且起点和终点相同的路径。简单环是一条(除了起点和终点必须相同之外)不含有重复顶点和边的环。

路径或者环的长度为其中所包含的边数。大多数情况下,我们研究的都是简单环和简单路径井会省略掉简单二字。当允许重复的顶点时,我们指的都是一般的路径和环。当两个顶点之间存在一条连接双方的路径时, 我们称一 个顶点和另一个顶点是连通的。我们用类似出u-V-W-X的记法来表示u到x的一条路径, 用U-V-W-X-U表示u到V到W到x再回到u的一条环。

**③ **联通图

  • **连通图:**图中任意两个顶点之间皆存在路径。

    一幅非连通的图由若干连通的部分组成,它们都是其极大连通子图。一般来说,要处理一张图就需要一个个地处理它的连通分量(子图)。
    在这里插入图片描述

  • 任何连通图的连通分量只有一个,即是其自身
  • 非连通的无向图有多个连通分量。

是一幅无环连通图。互不相连的树组成的集合称为森林。连通图的生成树是它的一幅子图,它含有图中的所有顶点且是一棵树。图的生成树森林是它的所有连通子图的生成树的集合。
在这里插入图片描述

树是一种无环图。无环图一定是树吗?

联通图的生成树包含所有的顶点的树:边数为V-1。包含所有顶点,边数位V-1,一定是联通生成树吗?

一个图一定有生成树吗?

一个图一定有生成森林。

在这里插入图片描述
⑤稀疏图和稠密图

图的密度是指已经连接的顶点对占所有可能被连接的顶点对的比例。

在稀益明中,被连楼的厦点对很少;而在稠密图中,只有少部分顶点对之间没有边连接。一般来说,如果图中不同的边的数量在顶点总数V的一个小的常数倍以内,那么我们就认为这幅图是稀疏的,否则则是稠密的。但实际应用中稀疏图和稠密图之间的区别是十分明显的。我们将会遇到的应用使用的几乎都是稀疏图。
在这里插入图片描述
假设节点度最大为3,如果有3000个节点,有3000* 3/2 = 4500条边,最多有3000 * 2999/2=4498500条边。

树一定是稀疏图。

图的表示满足:

必须为可能在应用中碰到的各种类型的图预留出足够的空间;

Graph的实例方法的实现一定要快,它们是开发处理图的各种用例的基础。

①图的基本表示-邻接矩阵

在这里插入图片描述

我们可以使用一个K乘K的布尔矩阵。当顶点v和顶点w之间有相连接的边时,定义v行w列的元素值为true,否则为false。这种表示方法不符合第一个条件,含有上百万个顶点的图是很常见的,V^2个布尔值所需的空间是不能满足的。adj[v]= adj[w] = true


A[i]][j]= 1表示顶点i和顶点j相邻。

对于简单图:主对角线为0。对于无向图,矩阵关于主对角线对称。

空间复杂度为O(V^2)

②图的基本表示-边的数组

我们可以使用一个Edge类,它含有两个int实例变量。这种表示方法很简洁,但不满足第二个条件一要 实现 adj() 需要检查图中的所有边。

一个二维数组,第一维的大小是图中边的总数,第二位的大小是2,即边的两个顶点。数组是int型的,存储的边的两个顶点就是顶点数组中对应这两个顶点的索引。

③图的基本表示-邻接表

我们可以使用一个以顶点为索引的列表数组,其中的每个元素都是和该顶点相邻的顶点列表,这种数据结构能够同时满足典型应用所需的以上两个条件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lNNiuEnK-1604496668206)(无向图.assets/image-20201102002855539.png)]

邻接表可以实现快速查看两点是否相邻。空间复杂度为O(V+E)。

可以使用链表、哈希表(HashSet)-O(1)和红黑树(TreeSet)-O(logV)来进行实现。

链表查找一定是线性的,所以想要加快查询的速度就不要使用链表。改为使用红黑树和哈希表。红黑树和哈希表的差别其实不大。可根据自己的习惯进行选择。我们这里选择红黑树,红黑树是保持了顺序性的并且更省空间。txt如下。

7 9
0 1
0 3
1 2
1 6
2 3
2 5
3 4
4 5
5 6

代码:Graph

import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import java.util.TreeSet;

//基于邻接表实现的图表示
public class Graph {
   

    private int V; //顶点数
    private int E; //边数
    private TreeSet<Integer>[] adj; //和顶点相邻的所有顶点

    public Graph(String filename){
   
        File file = new File(filename);
        try(Scanner scanner = new Scanner(file)){
   
            //获取到顶点数
            V = scanner.nextInt();
            if(V < 0) throw new IllegalArgumentException("V must be non-negative");
            adj = new TreeSet[V];//创建容量为V的红黑树
            for(int i = 0; i < V; i ++)
                adj[i] = new TreeSet<Integer>(); //每个存放顶点的红黑树

            E = scanner.nextInt();
            if(E < 0) throw new IllegalArgumentException("E must be non-negative");

            //获取边
            for(int i = 0; i < E; i ++){
   
                int a = scanner.nextInt();
                validateVertex(a);//判断顶点a是否合法
                int b = scanner.nextInt();
                validateVertex(b);//判断顶点b是否合法

                //自环边,本节只讨论简单图
                if(a == b) throw new IllegalArgumentException("Self Loop is Detected!");
                //平行边,本节只讨论简单图
                if(adj[a].contains(b)) throw new IllegalArgumentException("Parallel Edges are Detected!");

                adj[a].add(b); //b是a的邻边,b加到adj[a]
                adj[b].add(a); //a是b的邻边,a加到adj[b]
            }
        }
        catch(IOException e){
   
            e.printStackTrace();
        }
    }

    //判断该顶点是否合法。后面复用,这里用public
    public void validateVertex(int v){
   
        if(v < 0 || v >= V)
            throw new IllegalArgumentException("vertex " + v + "is invalid");
    }

    //图的顶点数
    public int V(){
   
        return V;
    }
    //图的边数
    public int E(){
   
        return E;
    }

    //两点间是否有边
    public boolean hasEdge(int v, int w){
   
        validateVertex(v);//判断点是否合法
        validateVertex(w);
        return adj[v].contains(w);
    }

    //获取顶点的所有相邻顶点
    public Iterable<Integer> adj(int v){
   
        validateVertex(v);
        return adj[v];
    }

    //获取顶点的度数(有个相邻定点)
    public int degree(int v){
   
        validateVertex(v);
        return adj[v].size();
    }

    @Override
    public String toString(){
   
        StringBuilder sb = new StringBuilder();

        sb.append(String.format("V = %d, E = %d\n", V, E));
        for(int v = 0; v < V; v ++){
   
            sb.append(String.format("%d : ", v));
            for(int w : adj[v])
                sb.append(String.format("%d ", w));
            sb.append('\n');
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值